Skip to content


WebKit on the iPhone (Part 2)

In the first part of this tutorial you’ve learned that you can use JavaScript code to get information about the web page and to modify links which would be normally ignored by UIWebView (because they are supposed to open a new window) so that they will work just like any other link.

In the second part of the tutorial you’ll learn how to open links (which are supposed to open in a new window) in a new tab. On the iPhone you can’t open multiple windows, so we have to use “tabs” instead of windows. I’ll also show how to deal with opening windows or tabs via JavaScript when no HTML link is involved.

In part one we’ve written the JavaScript function “MyIPhoneApp_ModifyLinkTargets()” which loops through all links and replaces the link target “_blank” with “_self”. This way the links will open in the same tab and are no longer ignored by UIWebView. But if we really want to open these links in new tabs, we have to open a new  tab ourselves and then open the link there. This can’t be directly done within the JavaScript code. So we have to find a way to pass the information about the link URL and the link target to the Objective-C part of our app. The easiest way to do this to replace the original link URL by a new URL which includes all these information. In order to make it easy to recognize our modified URLs, we are creating the new URLs with a new custom URL scheme “newtab”. We just add this to the existing JavaScript function we’ve written in the last part of the tutorial.

function MyIPhoneApp_ModifyLinkTargets() {
    var allLinks = document.getElementsByTagName('a');
    if (allLinks) {
        var i;
        for (i=0; i<allLinks.length; i++) {
            var link = allLinks[i];
            var target = link.getAttribute('target');
            if (target && target == '_blank') {
                link.setAttribute('target','_self');
                link.href = 'newtab:'+escape(link.href);
            }
        }
    }
}

The additional line

link.href = 'newtab:'+escape(link.href);

will take the original URL and escapes all the characters with a special meaning (like “:” and “/”) and puts the new custom URL scheme “newtab” in front of it. The link URL “http://www.apple.com/” will be converted to “newtab:http%3A//www.apple.com/” for example.

Within the Objective-C code of our app, we have to implement the UIWebView delegate method

- (BOOL)webView:(UIWebView *)view shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

This delegate method is called whenever a link is tapped by the user. And the app can check the URL and return YES or NO, wether it is OK to open this URL or not. What we need to do is to check if the URL is one of the URLs which were modified by us.  If this is the case we create a new tab with a new UIWebView object and then open the original URL in this new UIWebView object. Finally we return NO as the result of the delegate method to indicate that this modified URL should not be opened. This is important because the page is already loading in the new tab. If we find out that this delegate method is called with an URL that was not modified, we just return YES, so the URL will open as expected. We know that the URL was modified, if the URL uses the URL scheme “newtab”. So the delegate method would look like this:

- (BOOL)webView:(UIWebView *)view shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSURL *url = [request URL];
    if ([[url scheme] isEqualToString:@"newtab"]) {
        NSString *urlString = [[url resourceSpecifier] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        url = [NSURL URLWithString:urlString relativeToURL:[view url]];
        [self openNewTabWithURL:url];
        return NO;
    }
    return YES;
}

The “resourceSpecifier” method of the NSURL object is returning everything but the URL scheme, which is exactly what we need to get the original URL. And because within the JavaScript code we’ve escaped the original URL we need to unescape it. And this can be done with “stringByReplacingPercentEscapesUsingEncoding:“. The method “openNewTabWithURL:” is not shown here, but it is obvious what it is supposed to do: it creates a new tab with a new UIWebView object and opens the URL in the new tab.

In the above code you’ll notice this line:

url = [NSURL URLWithString:urlString relativeToURL:[view url]];

We call “[view url]” here (calling the method we’ve written in the first part of the tutorial) to get the base URL, in case we’ve received only a relative URL from the JavaScript code. For normal links this wouldn’t be necessary because the “href” property of a link element will always return the absolute URL. But when dealing with windows or tabs which are opened using JavaScript code and not from within links, it is possible to receive only relative URLs, and therefore we need to be able to create valid absolute URLs.

And now it’s time to explain how new windows can be opened with JavaScript code, without any HTML links. Many web pages are doing this, often for popup window with advertising banners, but also sometimes for important stuff. So we might be able to deal with this as well.

In JavaScript there’s the call

window.open(url,target,parameters)

Where “url” is the URL to open, “target” is the target, similar to the target attribute of HTML links and “parameters” are paramters which tell the browser the location and size of the new window and if the new window should open with or without toolbars, etc. When calling “window.open()” on the iPhone with a target that is a new window, nothing will happen. This is because the UIWebView object doesn’t support new windows or tabs. So we need to overwrite the the original “window.open()” function of JavaScript so our own code is executed whenever “window.open()” is called. Our own code will then check if the target will open a new window. If this is the case, we create a new URL with the scheme “newtab”, like we’ve done this with the links. And then we also need to explicitly open the new URL, which can be done by assigning the URL to the JavaScript property “location.href“.

function MyIPhoneApp_ModifyWindowOpen() {
    window.open =
            function(url,target,param) {
                if (url && url.length > 0) {
                    if (!target) target = "_blank";
                    if (target == '_blank') {
                        location.href = 'newtab:'+escape(url);
                    } else {
                        location.href = url;
                    }
                }
            }
}

Please note that this code makes certain assumptions: There are no HTML frames used in the web page and the only target names are “_blank”, “_self”, “_top”, “_parent”. Dealing with frames requires some extra work and would make things more complicated. So this should be handled in a later tutorial.

In the delegate method “webViewDidFinishLoad:” we just need to call the JavaScript function “MyIPhoneApp_ModifyWindowOpen()” as well. So we add a new line of code:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ModifyLinkTargets" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

    [webView stringByEvaluatingJavaScriptFromString:jsCode];

    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyLinkTargets()"];
    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyWindowOpen()"];
}

Now, whenever a web page uses the JavaScript call “window.open()“, to open a page in a new window, the delegate method “webView:shouldStartLoadWithRequest:navigationType:” is called, just like it is called when a link is opened. And because we’ve created a new URL with a “newtab” URL scheme when necessary, this is automtatically detected by the code we’ve written before.

What you’ve learned so far? You can write an iPhone app where a web page loaded in an UIWebView object is able to open new tabs, regardless if this is done using HTML links or via JavaScript using “window.open()”. The whole solution is more complicated than on the Mac where the WebView object is able to notify us about new windows or tabs which have to be created. But even if we have to use a mixture of Objective-C and JavaScript code to solve this task, it’s not too difficult.

Final notes:

There are still some details which are not covered by this tutorial. For example web pages can have “frames” and the targets referenced by links and “window.open()” can also address these frames. So you would need some additional code to check if a frame with the target name exists. If there’s a frame with the target name, then do not modify the link, if no frame exists, you should modify the link (using the “newtab” scheme) and treat the target name just like “_blank”.

Also “window.open()” will usually return a reference to the newly created window object so that the JavaScript code can access it later again. This can not be done on the iPhone using the iPhone SDK (at least not without violating the iPhone SDK agreement). But fortunately, most web pages don’t need to access the new windows later, so this limitation is usually not a big deal.

Posted in iPhone & iPod Touch, Programming.

Tagged with , , , .


60 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. iPhoneDev says

    Hi,

    Nice site and a lot of useful information.

    I’m still having issues though. Can you please help me create a Javascript function to open this in the same UIWebView:

    <a href=”‘+url+’” rel=”nofollow”>

    Neither of your examples seem to have any effect.

    Thanks

  2. Alexander Alexander says

    I think you should send me your code and an example HTML file by email, so I can see what exactly you’re doing. Posting HTML code in the comments will probably not come through properly, so I won’t see your problem.

  3. JedixJarf says

    Very nice tutorials, I would like to see how you do your URL filtering though :)

  4. Alexander Alexander says

    Good idea. I will explain this in my next blog post. It’s not that complicated ;-)

  5. user24080 says

    hey..
    nice tutorial!
    will this method open a new tab like in safari if i press a button?
    if not can you make a tutorial about that?
    can you provide the source code of this tutorial?
    greetz from austria

  6. Alexander Alexander says

    @user24080
    In one of the code snippets from above I call [self openNewTabWithURL:url];
    This is the method where you actually have to create a new Tab. This must be done by yourself. Basically you first create a new UIWebView object and add it into your view hierarchy and then load the URL in this new object. Of course you also have to make sure that only one of the UIWebView objects are visible at the same time and you need a way to allow the user to choose one of the UIWebViews (one of the Tabs) which should be made visible. I haven’t posted a code about all this, because this is highly depending on your requirements.

  7. wcasado says

    Thanks!

    Nice Tutorial help me a lot!

  8. Mark says

    there is an easy way to open a link in a new window – just press the link and hold couple of seconds

    http://ipod.about.com/od/webbrowsing/a/iphone-browser-new-window.htm

  9. Alexander Alexander says

    @Mark
    Yes, but this is a special feature of the Safari App. But this does not work in UIWebView. And when writing your own app, you have to use UIWebView and so you can’t open links in new windows/tabs this way. Press-and-hold the link will open the contextual menu in iPhone OS 3.x (but not in OS 2.x), but there won’t be any option to open the link in a new window. Opening new windows/tabs is not supported at all by the UIWebView API.

    And this is why I’ve written this blog post: To show a way to open links in new windows/tabs even if UIWebView does not support this.

  10. Mirko says

    Hi Alexander.

    I have one question regarding UIWebView.
    Is it possible to hide AutoFill toolbar on a particular UIWebView instance?

    http://img688.imageshack.us/img688/6050/img0378.png

    I noticed iCabMobile displays no AutoFill button.

  11. Alexander Alexander says

    @Mirko
    The toolbar cannot be switched off. It’s automatically displayed when activating a text box of a web page. And the “autofill” button is a special feature of Mobile Safari and is not available in apps which are using UIWebView. So UIWebView objects will never show the autofill button but will always have the toolbar (which contains “back”/”forward” and “Done” buttons).

  12. Mirko says

    Probably simulator bug, simulator shows AutoFill button, but its not visible on the device.

    Thanks!

  13. Marco says

    Is there a way to check/obtain/verfiy the SSL certificate ? (by using UIWebView).
    In iCabMobile you turn the color into green if the URL starts with ‘https:’.

    Also how safe is iCabMobile against a ‘man-in-the-middle’ attack ?

    Regards, Marco.

  14. Marco says

    @Marco (myself and I)

    It looks like certificate checking is done inside (beneath) UIWebView:

    - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
    {
    // load error, hide the activity indicator in the status bar
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    if (error.code == NSURLErrorCancelled) return; // this is Error -999

    // report the error inside the webview
    NSString* errorString = [NSString stringWithFormat:
    @"An error occurred:%@",
    error.localizedDescription];

    [myWebView loadHTMLString:errorString baseURL:nil];

    }

    An error occurred:
    untrusted server certificate

  15. Alexander Alexander says

    @Marco
    There’s no public API for checking/getting/inspecting the SSL certificate. So you have to rely on the capabilities of UIWebView and the server to do this. You can’t do your own checks. At least if the certificate is invalid, the delegate method “webView:didFailLoadWithError:” is called, so you can show a suitable error message.

    This means, in case UIWebView is vulnerable against man-in-the-middle-attacks, then all apps using UIWebView are as well.

  16. Kevin says

    Hello Alexander,
    great tutorial ! I have the following question. Is it possible to inject a different background and font color ? And how a sample script will look like ? Thank You for your great tutorials !

  17. Alexander Alexander says

    @Kevin
    In general you can inject any JavaScript code like I’ve described it in this blog post. And because colors can be set either by HTML attributes or by CSS properties, you would have to change either the HTML attributes or CSS properties. This can be done from within JavaScript.

    For example the following JavaScript code can be used to inject any kind of CSS rules using the general
    CSS syntax (see the last line). So with some knowledge about CSS you can do almost everything to the layout:

    var newStyles = document.createElement("style");
    newStyles.setAttribute( "type", "text/css" );
    document.getElementsByTagName("head")[0].appendChild(newStyles);
    var numStyles = document.styleSheets.length;
    document.styleSheets[numStyles-1].insertRule("body { color: blue; background-color: yellow; }", 0);
    
  18. Kevin says

    Hi Alexander !
    Just Great !! Thank You ! Your blog and friendly responses helped me a LOT !

    Best Regards !

  19. Gurpartap Singh says

    Hi Alexander,

    How would you suggest I can differentiate the mainFrame among every call that the UIWebViewDelegate methods receive?

    Thanks and best wishes for iCab Mobile success!

  20. Alexander Alexander says

    @Gurpartap Singh
    You can not find out if the delegate is called from the main frame or another frame of the page with the UIWebView API. This can be a problem because the delegate is called for other frames than the main frame as well (if a page uses frames). I’d call this a bug (and I have reported this to Apple, though I don’t expect that Apple will change this soon), because “webViewDidFinishLoad:” can be called multiple times for a single web page and only the last call of this method really means that the webView did finish loading. The calls before did only mean that a frame did finish loading, but you don’t have the opportunity to find out if this call is coming from a frame or the whole page.

    You can only see which UIWebView object is belonging to the current call of a delegate method in case you’ve multiple UIWebView objects.

  21. Gurpartap Singh says

    Well,

    That leaves me totally confused how one can build a browser without knowing that information. Doesn’t it confuse the code what to load, and what to add to UIWebView. I see you are using manual downloading of webpage rather than UIWebView’s loadRequest:. How do you handle frames other than mainFrame? :S I would also file this as a bug.

    I also wonder why there aren’t much discussions about it around the net.

  22. Gurpartap Singh says

    This is what I have reported to Apple (remove the comment if it doesn’t suite, since maybe it’s too long):

    Summary:
    # Consider, a webpage having multiple frames (or having adsense like ads, etc.). When loaded in a UIWebView, each of the frame starts calling the methods under UIWebViewDelegate.
    # There is no way to differentiate the frame being loaded in these delegate calls, resulting in confusion to the application’s logic where there is no way to find even a bit of workaround (unless you totally stop using UIWebView’s loadRequest: method.
    # This behavior is very unlike “WebView” class’ “frameLoadDelegate”, where each delegate method accepts a “WebFrame” parameter which lets the developer know if this frame is the main frame of the actual web page being loaded.

    Steps to Reproduce:
    # URL to use(or any other with frames/adsense ads): http://mashable.com/2010/01/11/social-media-integration/
    # Load it in a UIWebView using loadRequest: method.
    # Log the UIWebViewDelegate methods like webView:shouldStartLoadWithRequest:navigationType:.

    Expected Results:
    # I would expect it to either launch just once, for the main frame only; or
    # I would expect it to let me differentiate that which frame is this delegate method being called for, so I don’t do actions on adsense like ads, which I would do on the full web page in subsequent delegate calls like webView:didFinishLoading…

    Actual Results:
    # The delegate methods are called multiple times without any information to differentiate between the frame for which it is called.

    Regression:
    # I haven’t met a situation where I can get the things working in order.
    # The only way I believe is to use WebView’s frameLoadDelegate, which is probably not the solution, since it is not a documented API.

    Notes:
    # WebView’s frameLoadDelegate should be exposed to developers for building better working applications making use of UIWebView, especially browser applications.

  23. Alexander Alexander says

    @Gurpartap Singh
    Yes, you’re right. It is a little bit strange if all information about frames is hidden in the UIWebView API. I assume this is because Apple has never meant that the UIWebView class would be used for a real browser. They probably simply wanted to have an easy to use “rich text” text field.

    Not to have the information about frames makes things a little bit more difficult, but for all the basic features you don’t really need this information. And for some more advanced features you have to use JavaScript to find out if the page has frames. And of course there are a few things you just can’t do.

  24. Gurpartap Singh says

    @Alexander
    Thanks for the positive reply. It feels right that they didn’t intend to provide all features in UIWebView for developers. Pretty frustrating, but I can try with some javascript to detect frames.

    I’m afraid if I’ll be able to detect them when some external JS adds one, (like an adsense ad, tweetmeme widget or something else).

    Do you use IRC or jabber? I would appreciate to talk a bit.

    Best wishes.

  25. Alexander Alexander says

    @Gurpartap Singh
    I’m not a IRC or jabber user. I prefer the communication via email ;-)
    Just use the address alexander@icab.de

  26. Chandu says

    Hi Alexander,
    Your posts have been very useful. One of the things on Webkit i would like to ask you is regarding “changing useragent” when using uiwebview. I saw lot of posts and tried playing with NSMutableURLRequest/NSURLRequest and shouldStartLoadWithRequest but still having problems. I saw that iCabMobile does support desktop safari useragent with no issues. Is there some tweak here …

  27. Alexander Alexander says

    @Chandu
    I’ve just written a new blog post about this topic. This should answer your question ;-)

  28. Martt says

    Hello Alexander.
    Great app, it contains a lot of things missing in safari !

    In my app, I’m using an UIWebView which must connect and accept an unknown certificate. But I does’nt… How can I manage an “always accept” way ?

  29. Alexander Alexander says

    I think you can’t accept an unknow certificate once so that it will be accepted later automatically. This is because you can’t add a new certificate into the certificates database of the iPhone from within an App.

    What you can do is to write a category for the NSURLRequest class and implement a method

    + (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;

    If this methods returns YES for a certain host, then a Certificate is accepted even if it is unknown or invalid. Of course you should never simple return YES without the user has confirmed that he/she accepts the unknown/invalid certificate. So always check the “error” parameter of the delegate method “webView:didFailLoadWithError:” which will tell you if there’s an issue with the certificate (for example [error code] could be equal to NSURLErrorServerCertificateUntrusted). Then ask the user if the certificate should be accepted even if it is not valid/unknown. If the user accepts the certificate, you may store the host as a “safe” one and try the request again. And within “allowsAnyHTTPSCertificateForHost:” only return YES for these “safe” hosts.

  30. Venkat says

    Hello Alexander,
    This is nice post.
    But i have a requirement in my iPhone App that the SSL certificate details(like issued to,issued,validity) of the https enabled webpage should be read and stored into my app. This could be done in background process or at the time of loading the webpage in UIWebView controller. Is it possible? If so, can you please provide the Objective-C code to get the details.

  31. Alexander Alexander says

    @Venkat
    You can not get the SSL certificate details from a UIWebView object. But if you use NSURLConnection to load data from the web, you’ll get notified about authentication request and so you can use the NSURLCredentialStorage to store the authentication information. But getting the peer certification chain isn’t possible, as far as I know.

  32. Jame says

    Hi Alexander,
    I think function “window.open” don’t work. I placed a “alert(…)” in “function MyIPhoneApp_ModifyWindowOpen(){…}” but app don’t show any alert. Also, I have some question. What happen when execute javascript statement “window.location = url” on website? After calling the statement, method “window.open()” is called? In this case, how to open link “url” in new tab?
    Thank you so much!

  33. Alexander Alexander says

    @Jame
    window.open is called whenever the JavaScript code uses window.open() to open a popup window. Normal links or using location.href will not call window.open. For links the function MyIPhoneApp_ModifyLinkTargets() is responsible.

  34. Jame says

    Hi Alexander,
    Yes, your function MyIPhoneApp_ModifyLinkTargets() is good solution for normal links.
    But how to fetch link when javascript code “window.location = url” is called?
    Thank you so much!

  35. Alexander Alexander says

    @Jame
    Assignments to “window.location” will never open in new Tabs. These will open the URL in the current page of the “window”, and “window” is the reference to the window that page is located. So this is similar to a normal link without a TARGET attribute.

    So an assignment to window.location will call the delegate method “webView:shouldStartLoadWithRequest:navigationType:” just like a tap on a link.

  36. Jame says

    Yes, I’ve just known it.
    Thank you so much!

  37. allen says

    I just come to ask how you get the progress of loading a webpage?
    Thx a lot.

  38. Alexander Alexander says

    @allen
    This is somehow complicated, especially if your only interested in getting “progress” information. In iCab, getting the progress information is only a “side effect” of many other features.

    You might look at the latest version of the Terra Browser which no longer shows a progress bar but instead shows a nicely designed “spinning-wheel” busy indicator, after Apple forced its developer to no longer use private APIs. This is an easy solution if you only need some kind of busy indicator.

    In iCab, all URL connections that are initiated are intercepted and “routed” through iCab’s own code, so iCab is able to modify all HTTP requests and to receive all the HTTP responses before UIWebView is able to process the data. This can be done by installing your own HTTP protocol handler using the NSURLProtocol class. iCab needs to do this to overcome all the limitations and restrictions of the UIWebView. And when iCab can directly access all the requests and responses, it has also access to the information about amount of data that is currently received and it can calculate the “progress” information. Of course this has also some flaws, because you just can not know from which UIWebView instance the requests are coming from (in case there are multiple instances loading data at the same time) and also because the UIWebView only opens a certain number of connections at the same time, you don’t know how many files have to be downloaded as well to fully load a page (so you even don’t know what “100% progress” means). And it’s not easy to create a usable progress indicator from such poor data. You have to adapt your calculations over time when more and more files are loaded. For example: don’t expect you can draw the progress indicator at 50% just because the first batch of files the UIWebView is able to load at the same are loaded for 50%. It is likely that the web page has much more files to load, so the real progress might be only 5% or 10% (but you don’t know this). Also if you’ve already displayed the progress indicator at a certain percentage and new larger files will be loaded which tell you that the real progress is much lower than the progress that is already shown to the user, you can not go back (this would be annoying for the user). Also because the UIWebView only loads a few files at the same time, the progress you can calculate over the time will jump in larger steps (forward but sometimes also back). So you need to make this visually much smoother. As I said, this is relatively complicated.

  39. gema says

    Hello Alexander,
    I noticed that iCab Mobile can use rich text editors for forums like this example:
    http://www.kevinroth.com/rte/demo.htm
    I’m trying to use it in a UIWebview but the keyboard doesn’t appear when you tap on the edit text area. It’s inside a “iframe” so maybe that’s the problem…any clue on your side?

    Thanks in advance!

  40. Alexander Alexander says

    @gema
    No, iCab does not support these rich text fields and no other iOS browser does. The way how the iOS activates the virtual keyboard makes it impossible to use these rich text editors.

    But the demo page of this rich text editor is able to detect an iOS browser and then simply falls back to a regular “textarea” element of HTML, so you can enter text on iPad/iPhone as well, but here it’s a standard text field, not a rich text field.

    The web page probably uses the “UserAgent” information to find out if the page is running on and iPad/iPhone and maybe other touch-based devices.

    The problem with rich text fields on touch.based devices with a virtual keyboard is that on these devices the keyboard is not attached and available all the time. The keyboard only opens when you actively activate a text field. But “rich text editors” on web pages are not “real” text fields, these are in Javacript programs which build up some HTML and CSS code and render it in an iframe so this whole thing looks like a text field with lots of properties and settings for all the features. But it’s not a “real” text field the iOS would know of. The Rich text editor is then listening for keyboard events on document or window level, which is the main issue on touch-based devices with virtual keyboards. On a desktop computer where the keyboard is available all the time, you can create keyboard events all the time by pressing a key, even if no text field is “listening”. The OS delivers these keyboard events also to GUI elements or objects which are not textfields. But on devices with virtual keyboards this does not work, because the keyboard is only activated when a textfield is activated, and in order to make rich text editors work, the virtual keyboard would have to be available all the time, even when no textfield is active.

  41. Steven says

    Hi Alexander,

    Awesome blog and app.

    I’m wondering if it’s possible with UIWebview on iOS to intercept the web page request so as to re-direct to a proxy and feed the request there. i.e. http://www.bbc.com should go to proxy 192.168.1.10:1234 and use NTLM authentication.

    I’ve tried by doing this in shouldStartLoadWithRequest using ASIHTTPRequest & loadHTMLString but that doesn’t bring back resources (images, css etc).

    Is this even possible with UIWebView on iOS?

  42. Alexander Alexander says

    @Steven
    Check out the NSURLProtocol class. Using this class you can write your own HTTP protocol handler, so all HTTP requests can be routed through your own code.

  43. Brock says

    Does the above work on “javascript:void(0);” What you describe above is what I’m trying to get my app to do. However, I’m lost on implementing your tutorial.
    Per instructions on webkit iphone part 1,
    1.created MyWebViewAdditions.h (.m) (pasted code from part 1)
    2.created ModifyLinkTargets.js (pasted code from part 2)
    3. I’m not sure of where to put the delegate methods though. (in my existing apps delegate.m file or the MyWebViewAdditions.m file?

    very nice tutorial. I’m following what you’re doing, just not how.

  44. Alexander Alexander says

    @Brock
    What exactly do you want to do with “javascript:void(0);”?

    The delegate methods are those from the UIWebView class. You have to implement some of the delegate methods from your UIWebView class to be notified when the page has finished loading and you can execute the JavaScript code.

  45. Brock says

    @Alexander
    I am trying to open a link to pdf documents in a work website. The links all copy as “javascript:void(0)”. After clicked and loaded the link is to a specific document, one in particluar being “http://incapture.epcounty.com/incaptureweb/ImagePDF/cjj5jn555xyb2nvibtrtb321.pdf”

    I understand and have seen UIWebView methods implemented in the controller file, correct? Specifically, in my app “Story_2_testViewController.m” is where I’m placing your method to access the ModifyLinkTargets.js file. Right before my viewDidLoad method.

  46. Alexander Alexander says

    @Brock
    OK, nobody can prevent that web developers are doing stupid things ;-)

    I guess the web page is using JavaScript to open the PDF files via onClick handler and uses these these handlers in “A” tags. And because the onClick handlers are already opening the PDF file, the HREF attribute seems to be useless and so the web developers inserted “javascript:void(0)” as URL in these HREF attributes, which is definitely not what should be done in this case. This is just a bad practice and also introduces usually many issues and should be avoided. One issue that you can no longer copy the link URL, because there’s no link URL anymore. And for UIWebViews this means you can’t directly control where these links will open, because the “links” itself are just “dummies”.

  47. Brock says

    @Alexander

    Thanks. If I have access to the web developer, what do I have to get him to do to implement a better practice so that I could grab the link? Just use the HREF attribute? Is that something that could be done if the source files are being pulled from a database and those are the pdf links that are being opened? I.e. this is a court system that pulls records from an archaic database and displays the PDFs from the links.

    Also, you’re making me feel better about my limited programing as I can get webkit to open links that usually open in new windows, but I wasn’t able to get those to work.

  48. Alexander Alexander says

    @Brock
    What the page is doing is probably something like this:

    <a href=”javascript:void()” onclick=”location.href=url”>

    which should be just be done this way:

    <a href=”url”>

    Or if they need the onclick hander, they should return the value “false” for the onclick handler to tell the browser that the standard behavior of the browser (open the page from the HREF attribute) should not be done. Then they can just put the real URL into the HREF attribute and open the URL via onclick handler. Then the links would also work when javaScript is switched off, then the link your also work when it is used outside of the context of the web page, like when a browser reads the link via contextual menu to open it in a new tab or the user wants to copy & paste the like etc.

  49. Brock says

    @Alexander

    Ok, back to the drawing boards. I downloaded your app and it works fine on the page as is. Very nice app by the way. I’m going to keep fooling around and see if I can’t get things working.

  50. Frank says

    Hi Alexander,

    Thanks for your awesome blog, it is very helpful!
    I am wondering how to catch the url of links like <a href=”#content” rel=”nofollow”>Skip to this page’s content</a>. shouldStartLoadWithRequest would not be called in this case.

    Thanks for your help!

1 2



Some HTML is OK

or, reply to this post via trackback.