Confirm Javascript behavior in UIWebView

I am trying to execute some Javascript on content in a UIWebView. I may not know the running time of Javascript, but I'm not confused in the following example. See the source code and comments for more details:

NSString *html = [NSString stringWithFormat:@"<html><head><script type='text/javascript'>var content='the initial content';function myFunc(){return 'value of content: ' + content;}myFunc();</script></head><body>Hello blank</body></html>"]; // I would expect after this call that the variable content exists, as well as the function myFunc() [self.webView loadHTMLString:html baseURL:nil]; // However, it appears not to. The following call returns nothing NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"myFunc();"]; NSLog(@"result: %@", result); // Inserting the Javascript at this point seems to work as expected result = [self.webView stringByEvaluatingJavaScriptFromString:@"var content='the new content';function myFunc(){return 'value of content: ' + content;}myFunc();"]; NSLog(@"result: %@", result); // And calling myFunc() at this point is successful result = [self.webView stringByEvaluatingJavaScriptFromString:@"myFunc();"]; NSLog(@"result: %@", result); 

To summarize, I would expect the global variable and Javascript function created when the html was loaded to be available for subsequent javascript calls. Surprisingly, this does not seem to be the case, and all javascript content should be added later. Is it correct?

Thanks for any help in advance.

----- update ----- I have to add that self.view is a WebView in this case. In addition, I tried to put the script tag in the head and body of the HTML document without changing behavior.

+6
javascript iphone cocoa-touch uikit uiwebview
source share
2 answers

The mclin answer is correct that the UIWebView is performing loads in the background. If everything you download is simple html and the tags have no content like src = "...", then you can be pretty sure that the webViewDidFinishLoad method (the UIWebViewDelegate protocol method) will be called only once and that you can execute your javascript in this deletet method.

However, UIWebView, after loading the HTML bootstrap, will load any images, javascript, audio, video, etc., and webViewDidFinishLoad will be called when each of these subsequent loads is completed. I think if you look at the Safari progress bar on your iPhone, you can see this behavior. The progress bar goes about halfway after loading the start page, and then moves a little bit as the images or what you have downloaded.

I tried several methods to run javascript after the page has finished loading. It will be easier to make webViewDidFinishLoad execute another selector after a short delay and check the load property of webView. You take a little risk here by taking “time” and “flows” together, but hey ... if you want it easily (that's what it is).

 -(void) checkIfLoadDone:(UIWebView *) webView { if (webView.loading) { return; } // run your javascript here } -(void) webViewDidFinishLoad:(UIWebView *) webView { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(checkIfLoadDone:) object:webView]; [self performSelector:@selector(checkIfLoadDone:) withObject:webView afterDelay:0.5]; } -(void) webViewDidStartLoad:(UIWebView *)webView { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(checkIfLoadDone:) object:webView]; } - (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { // ooops ... probably should deal with the error, but lets just run checkIfLoadDone [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(checkIfLoadDone:) object:webView]; [self performSelector:@selector(checkIfLoadDone:) withObject:webView afterDelay:0.5]; } 

A more sophisticated method is to track loads individually and maintain a counter. When the counter returns to 0, you can run your javascript. In this case, you will need to implement the delegation method for

 - (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (navigationType != UIWebViewNavigationTypeOther) { self.outStandingLoads = 0; } return YES; } 

Now you can have webViewDidStartLoad: increment outStandingLoads and have webViewDidFinishLoad: and webView: didFailLoadWithError: decrement and checkStandingLoads. I would use the NSInteger atomic property for outStandingLoads in all code to eliminate the potential for loading more than one thread.

I think that any of the methods will work quite well. I chose the latter approach, but only because I needed to solve other problems in webViewDelegate calls. I decided to extend the UIWebView and make it my delegate, where my extension implemented the delegate methods. Then I use the NSNotification model to send notifications like "WebPageLoadBegan" and "WebPageLoadComplete" to communicate with my user interface code. It seemed to me a lighter model when working with two different sets of user interface code ... iPad and iPhone.

Hope this helps.

+3
source share

Your UIWebView did not complete the download.

When you call [ self.webView loadHTMLString:html baseURL:nil]; He does not download it immediately. It starts a thread to do this asynchronously, so it didn’t load when you evaluate your JS.

Make your view controller a UIWebView delegate, and then evaluate your JS at -(void)webViewDidFinishLoad:(UIWebView *)webView

+1
source share

All Articles