Uiwebview CSS Injection Using JavaScript

UIWebView CSS injection using JavaScript

To inject javascript into a web view you must explicitly write in the html.

An example of this is here where I wrote in a javascript scrollTo function:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
"script.type = 'text/javascript';"
"script.text = \"function myFunction() { "
"window.scrollTo(100,100)"
"}\";"
"document.getElementsByTagName('head')[0].appendChild(script);"];

[webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];}

This code writes the entire script tags and the myFunction into the head of the HTML

Injecting CSS into UIWebView using JavaScript

Your solution won't work because

  1. your cssNode.href should be a URL (i.e. escaped and prefixed with file://), not a path
  2. Safari doesn't let you load local files from a remote page, as it's a security risk.

In the past I've done this by downloading the HTML using an NSURLConnection, and then adding a <style> tag in the HTML head. Something like:

NSString *pathToiOSCss = [[NSBundle mainBundle] pathForResource:@"reader" ofType:@"css"];
NSString *iOSCssData = [NSString stringWithContentsOfFile:pathToiOSCss encoding:NSUTF8StringEncoding error:NULL];
NSString *extraHeadTags = [NSString stringWithFormat:@"<style>%@</style></head>", iOSCssData];
html = [uneditedHtml stringByReplacingOccurrencesOfString:@"</head>" withString:extraHeadTags];

[webView loadHTMLString:html baseURL:url];

Insert CSS into loaded HTML in UIWebView / WKWebView

You can do it like this:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *cssString = @"body { font-family: Helvetica; font-size: 50px }"; // 1
NSString *javascriptString = @"var style = document.createElement('style'); style.innerHTML = '%@'; document.head.appendChild(style)"; // 2
NSString *javascriptWithCSSString = [NSString stringWithFormat:javascriptString, cssString]; // 3
[webView stringByEvaluatingJavaScriptFromString:javascriptWithCSSString]; // 4
}

What this code does:

// 1 : Define a string that contains all the CSS declarations

// 2 : Define a javascript string that creates a new <style> HTML DOM element and inserts the CSS declarations into it. Actually the inserting is done in the next step, right now there is only the %@ placeholder. I did this to prevent the line from becoming too long, but step 2 and 3 could be done together.

// 3 : Combine the 2 strings

// 4 : Execute the javascript in the UIWebView

For this to work, your HTML has to have a <head></head> element.

EDIT:

You can also load the css string from a local css file (named "styles.css" in this case). Just replace step //1 with the following:

NSString *path = [[NSBundle mainBundle] pathForResource:@"styles" ofType:@"css"];
NSString *cssString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

As another option you can just inject a <link> element to the <head> that loads the CSS file:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *path = [[NSBundle mainBundle] pathForResource:@"styles" ofType:@"css"];
NSString *javascriptString = @"var link = document.createElement('link'); link.href = '%@'; link.rel = 'stylesheet'; document.head.appendChild(link)";
NSString *javascriptWithPathString = [NSString stringWithFormat:javascriptString, path];
[webView stringByEvaluatingJavaScriptFromString:javascriptWithPathString];
}

This solution works best for large CSS files. Unfortunately it does not work with remote HTML files. You can only use this when you want to insert CSS into HTML that you have downloaded to your app.

UPDATE: WKWebView / Swift 3.x

When you are working with a WKWebView injecting a <link> element does not work because of WKWebView's security settings.

You can still inject the css as a string. Either create the CSS string in your code //1 or put it in a local file //2. Just be aware that with WKWebView you have to do the injection in WKNavigationDelegate's webView(_:didFinish:) method:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
insertCSSString(into: webView) // 1
// OR
insertContentsOfCSSFile(into: webView) // 2
}

func insertCSSString(into webView: WKWebView) {
let cssString = "body { font-size: 50px; color: #f00 }"
let jsString = "var style = document.createElement('style'); style.innerHTML = '\(cssString)'; document.head.appendChild(style);"
webView.evaluateJavaScript(jsString, completionHandler: nil)
}

func insertContentsOfCSSFile(into webView: WKWebView) {
guard let path = Bundle.main.path(forResource: "styles", ofType: "css") else { return }
let cssString = try! String(contentsOfFile: path).trimmingCharacters(in: .whitespacesAndNewlines)
let jsString = "var style = document.createElement('style'); style.innerHTML = '\(cssString)'; document.head.appendChild(style);"
webView.evaluateJavaScript(jsString, completionHandler: nil)
}

Injection of JavaScript Functions Into a UIWebView Not working always

running web.html stored in Resource.

    NSString *path = [[NSBundle mainBundle] pathForResource:@"web" ofType:@"html"];
NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

[webView stringByEvaluatingJavaScriptFromString:jsCode];
[webView setDelegate:self];
[webView loadHTMLString:jsCode baseURL:nil];

in web.html, in body, you should have onload(), that will fire after webView start.

<html> <body> <head>

<script>

function anyFunction(){
window.location='testing123';
}

</script>

</head><body onload="anyFunction();">
</body></html>

from there, you should be able to grab "testing123".

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 
{
NSString *data = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([data isEqualToString:@"testing123"])
NSLog(@"value received");
}

Inject Objective-C data into a UIWebView that loads a local HTML file?

If the data to be injected is "safe", you could construct your "enormous NSString object" as a format string, sprinkled with %@ markers, and use stringWithFormat: to perform the injection in a single move. This is how I construct the pages in the TidBITS News app, using pieces that all come from RSS. It's really quite painless.

Injection of JavaScript Functions Into a UIWebView Not working always

running web.html stored in Resource.

    NSString *path = [[NSBundle mainBundle] pathForResource:@"web" ofType:@"html"];
NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

[webView stringByEvaluatingJavaScriptFromString:jsCode];
[webView setDelegate:self];
[webView loadHTMLString:jsCode baseURL:nil];

in web.html, in body, you should have onload(), that will fire after webView start.

<html> <body> <head>

<script>

function anyFunction(){
window.location='testing123';
}

</script>

</head><body onload="anyFunction();">
</body></html>

from there, you should be able to grab "testing123".

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 
{
NSString *data = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([data isEqualToString:@"testing123"])
NSLog(@"value received");
}


Related Topics



Leave a reply



Submit