zoul
zoul

Reputation: 104065

How can I reliably detect a link click in UIWebView?

I have a UIWebView and I need to do something when user taps a link. There’s a delegate callback that can be used to detect the taps:

- (BOOL) webView: (UIWebView*) webView
    shouldStartLoadWithRequest: (NSURLRequest*) request
    navigationType: (UIWebViewNavigationType) navigationType
{
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        …
    }
}

The problem is that this code doesn’t handle all link clicks. As an example, a plain Google Search results page does something weird with the links:

<a href="http://example.com/" class="l" onmousedown="return rwt(…)">
    <em>Link Text</em>
</a>

The rwt function results in the links not triggering the UIWebViewNavigationTypeLinkClicked event when tapped. Is there a way to reliably detect all events that fall into the “navigate to some other page” bucket?

Upvotes: 9

Views: 15441

Answers (4)

Aanchal Chaurasia
Aanchal Chaurasia

Reputation: 629

You need to add this line

webView.dataDetectorTypes = UIDataDetectorTypeAll;

then

  • (BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType

method will get call.

Upvotes: 1

prewett
prewett

Reputation: 1647

I had a bunch of problems getting this to work. Using a UITapGestureRecognizer works, except that it will always receive the taps, even for links. Unfortunately, links have about a 300 ms delay before they get recognized, which means that the gesture recognizer gets the taps before the link is recognized, which creates a timing problem (even worse because you shouldn't hard code a time to wait in case Apple changes it). Plus, waiting 300+ ms for the tap gives a poor user experience for my app.

So what I ended up doing was overlaying a transparent div on top of everything that gets the non-link taps. Then, put the links at a higher z-level. Finally, make sure everything uses ontouchend="javascript:window.location.href='...';". ontouchend will only act on a tap release, which is what users expect. Setting window.location.href loads a new page, ensuring that all taps call -webView:shouldStartLoadWithRequest:navigationType: and I can check the URL to see which I need to do.

The HTML looks like: ... ... ... ...

(I'm not totally sure if "position: relative" always places things in the same position as otherwise, but it worked nicely for my simple page; your mileage may vary. However, you will needs position:something in order to get z-index to work.)

Upvotes: 0

zoul
zoul

Reputation: 104065

So far I have arrived at the following solution. First, I inject some JS code into the page when loaded:

function reportBackToObjectiveC(string)
{
    var iframe = document.createElement("iframe");
    iframe.setAttribute("src", "callback://" + string);
    document.documentElement.appendChild(iframe);
    iframe.parentNode.removeChild(iframe);
    iframe = null;
}

var links = document.getElementsByTagName("a");
for (var i=0; i<links.length; i++) {
    links[i].addEventListener("click", function() {
        reportBackToObjectiveC("link-clicked");
    }, true);
}

When user taps a link, I know it in advance thanks to the webView:shouldStartLoadWithRequest: navigationType: delegate call:

if ([[[request URL] scheme] isEqualToString:@"callback"]) {
    [self setNavigationLeavingCurrentPage:YES];
    return NO;
}

Then if another request comes and _navigationLeavingCurrentPage is true, I know the user has clicked a link even though the navigation type flag is UIWebViewNavigationTypeOther. I still have to test the solution extensively, for I’m afraid that it will lead to some false positives.

Upvotes: 6

Lefteris
Lefteris

Reputation: 14677

I believe this can be done, by embedding in the HTML code of the website a custom JavaScript which will monitor the events, and based on which event you want to monitor, it could trigger a page redirect with a custom URL scheme, which you can intercept in the shouldStartLoadWithRequest.

Something like this:

<script>
// Function to capture events
function captureEvent(el) {
  window.location.href="callback://"+el.href;  
}

var elms = document.getElementsByTagName("a"); 
for (var i=0; i<elms.length; i++) {
  elms[i].addEventListener("onmousedown", function(){captureEvent(el)}, true); 

 }
</script>

Then in the shouldStartLoadWithRequest, you can search for NSURLRequest's that have a callback:// url scheme, and do whatever you want.

This has not been tested, but something like this might get you in the right direction.

Also, since this was mentioned, yes you can add your custom script to any webpage, by using this:

- (void)webViewDidFinishLoad:(UIWebView *)webView 
{
    [super webViewDidFinishLoad:webView];    
    [webView stringByEvaluatingJavaScriptFromString:@"document.body.insertAdjacentHTML('BeforeEnd','<script>....</script>');"];
}

Upvotes: 1

Related Questions