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
- your
cssNode.href
should be a URL (i.e. escaped and prefixed withfile://
), not a path - 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
Bug with Chrome's Localstorage Implementation
Use a New CSS File to Override Current Website'S
Playing HTML5 Audio in Android Browser
Why Aren't Safari or Firefox Able to Process Audio Data from Mediaelementsource
Googlemaps Does Not Load on Page Load
Running JavaScript in New Window.Open
How to Convert Numbers into Scientific Notation
How to Add Validation/Restrictions for HTML5 Date Field Without Jquery/Javascript
Is It the Last 'Script' Element the Currently Running Script
Storing Arbitrary Info in HTML Tags for JavaScript
Trying to Set a Span Element Equal to a Variable Value in a Js Rock, Paper, Scissors Game
How to Add HTML Inside a Title Attribute
Phantomjs Page.Content Isn't Retrieving the Page Content
Good Beginners Tutorial to Socket.Io