iPhone Safari Web App opens links in new window

130,636

Solution 1

I found JavaScript solution in iWebKit framework:

var a=document.getElementsByTagName("a");
for(var i=0;i<a.length;i++)
{
    a[i].onclick=function()
    {
        window.location=this.getAttribute("href");
        return false
    }
}

Solution 2

The other solutions here either don't account for external links (that you probably want to open externally in Safari) or don't account for relative links (without the domain in them).

The html5 mobile-boilerplate project links to this gist which has a good discussion on the topic: https://gist.github.com/1042026

Here's the final code they came up with:

<script>(function(a,b,c){if(c in b&&b[c]){var d,e=a.location,f=/^(a|html)$/i;a.addEventListener("click",function(a){d=a.target;while(!f.test(d.nodeName))d=d.parentNode;"href"in d&&(d.href.indexOf("http")||~d.href.indexOf(e.host))&&(a.preventDefault(),e.href=d.href)},!1)}})(document,window.navigator,"standalone")</script>

Solution 3

If you are using jQuery, you can do:

$("a").click(function (event) {
    event.preventDefault();
    window.location = $(this).attr("href");
});

Solution 4

This is working for me on iOS 6.1 and with Bootstrap JS links (i.e dropdown menus etc)

$(document).ready(function(){
    if (("standalone" in window.navigator) && window.navigator.standalone) {
      // For iOS Apps
      $('a').on('click', function(e){
        e.preventDefault();
        var new_location = $(this).attr('href');
        if (new_location != undefined && new_location.substr(0, 1) != '#' && $(this).attr('data-method') == undefined){
          window.location = new_location;
        }
      });
    }
  });

Solution 5

This is an old question and many of the solutions here are using javascript. Since then, iOS 11.3 has been released and you can now use the scope member. The scope member is a URL like "/" where all paths under that scope will not open a new page.

The scope member is a string that represents the navigation scope of this web application's application context.

Here is my example:

{
  "name": "Test",
  "short_name": "Test",
  "lang": "en-US",
  "start_url": "/",
  "scope": "/",
  ...
}

You can also read more about it here. I also recommend using the generator which will provide this functionality.

If you specify the scope, everything works as expected similar to Android, destinations out of the scope will open in Safari — with a back button (the small one in the status bar) to your PWA.

Share:
130,636

Related videos on Youtube

Pavel Linkesch
Author by

Pavel Linkesch

Frontend engineer engineering frontend at Maker. JavaScript, ReasonML, React, web components.

Updated on August 29, 2020

Comments

  • Pavel Linkesch
    Pavel Linkesch almost 4 years

    I have problem with web after adding icon to Home Screen. If the web is launched from Home Screen, all links will open in new window in Safari (and lose full screen functionality). How can I prevent it? I couldn't find any help, only the same unanswered question.

    • Amir Raminfar
      Amir Raminfar about 6 years
      You can now use the scope parameter in manifest.json. See my answer for more details. I have tested it in iOS 11.3 and it does work.
    • Chris Haines
      Chris Haines about 6 years
      To reiterate, for anyone struggling with iOS 11.3 opening Safari, please see @AmirRaminfar's answer here: stackoverflow.com/a/49604315/32055
  • Alex
    Alex over 13 years
    This was a GODSEND! Thank you so much!
  • ajcw
    ajcw almost 13 years
    Please explain why .live() might be better?
  • msaspence
    msaspence almost 13 years
    live will bind the event to all links including those that don't exist yet, click will only bind to ones that currently exist
  • Steve
    Steve almost 13 years
    thanks! lifesaver. I just spent hours trying to figure out why safari was loading all the time.
  • Oskar Austegard
    Oskar Austegard almost 13 years
    To state the obvious and make this explicit: iOS treats links in Web Apps as something that should be opened in Safari, and javascript location changes as an in-app action that is allowed to saty in the web-app. The code above works because it prevents the default link behavior, replacing it with a js nav call.
  • Clay
    Clay over 12 years
    For Rails 3.1 apps, this seems to break traditional delete links and redirects them to the show action instead.
  • Max888
    Max888 over 12 years
    Is there an example of the opposite? Forcing an iPhone web app to open a page in Safari eventhough it's a javascript location change?
  • cmplieger
    cmplieger over 12 years
    @Pavel thank you for mentioning iwebkit :). Helps to get some traffic :D
  • Matt White
    Matt White over 12 years
    I found this script didn't work for iOS5. This solution did work though: stackoverflow.com/a/8173161/449330
  • Jonathan
    Jonathan about 12 years
    This works great except for one page, the "Contact us" page for our company. Instead of showing the page, it opens the application "Maps" and pinpoints our office. What could be causing this, and how can we fix it?
  • rmarscher
    rmarscher about 12 years
    @Jonathan I'm not sure. Does it not happen if you remove this script? Maybe post a link to your site? Or open a new question, that might be better.
  • Jonathan
    Jonathan about 12 years
    @rmarscher This only happens when running the code you provided and not without it. I'm a web developer myself and I don't understand why it would handle the link this way. I don't have a page URL because it's currently not running the code so you won't notice it. Also, it affects the regular Safari as well, and not only standalone. Thanks for your answer!
  • Ian
    Ian about 12 years
    Good addition to the above solutions. Needed a domain check to keep people from opening outside sites in the app. Also, works on iOS 5.
  • drusepth
    drusepth about 12 years
    If you're doing javascript-y things and have href="#", you might want the following modification if this script is breaking their functionality: thunked.org/p/view/pub/uccr0k3sr
  • Chim Kan
    Chim Kan about 12 years
    Thanks a lot! This is the only code that worked for iOS5 w/ Twitter Bootstrap. It doesn't work on production though.
  • daformat
    daformat about 12 years
    Mmm not so sure why it would not work in production but I think it's something else. Do let me know!
  • user3167101
    user3167101 about 12 years
    [].forEach.call(document.links, function(link) { link.addEventListener("click", function(event) { event.preventDefault(); window.location = this.href; }) });
  • Lukasz Korzybski
    Lukasz Korzybski almost 12 years
    works on iOS 5 for me too. The problem sometimes might be with the cache. While testing different approaches I was unable to force iOS to invalidate its cache and retrieve new version of JS files (Safari did pick up the changes but no more after adding app to Home Screen). Changing port of my dev server helped. If you have max-age=0 set (or equivalent) then this probably won't affect you.
  • Fenton
    Fenton over 11 years
    +1 from me - used this.href rather than casting to a jQuery object, but thanks for this answer. Works on iOS6.
  • sradforth
    sradforth over 11 years
    This should be the accepted answer and worked a charm on my iPad1 fullscreen client made with PHPRunner by placing the code in the header. Not sure why it's so obfuscated as it seems quite concise bit of code that could be written legibly without much BW overhead... that's just being picky though and generally really want to say thanks.
  • Sean
    Sean over 11 years
    This breaks Bootstrappy things such as href="#" links that are used by js functions
  • Sean
    Sean over 11 years
    Luckily my workmate had some code lying around. i will add an answer
  • Tom Davies
    Tom Davies over 11 years
    .live() is deprecated as of jQuery 1.7, and removed as of 1.9. Use $(document).on('click', 'a', function(){...}) instead.
  • pingu
    pingu about 11 years
    Does this have any side effects?
  • saulob
    saulob about 10 years
    Perfect, you saved me :)
  • Trolley
    Trolley about 10 years
    +1. This actually checks whether you're using a webapp before fixing the links.
  • Joel Murphy
    Joel Murphy over 9 years
    Works in iOS 8.0.2! Thanks
  • nematoth
    nematoth almost 9 years
    @sean I have another webapp running in an iPad which is using an image map as href and this code doesn't work..It works fine for all other links. Any ideas how to make this code work with image maps? I have tried copying the whole chunk and replacing $('a').on('click', function(e){` with $('area').on('click', function(e){` but that doesn't seem to work either. Any ideas?
  • paulalexandru
    paulalexandru over 8 years
    This works perfectly if you use it like this. If I tried to include this code from a js file it did not worked anymore. (just wanted to tell everybody no to do this because i waisted a lot of time finding out this)
  • user1788736
    user1788736 over 8 years
    @Pavel Linkesch May I know how should run the above code once user click on a hyperlink ?
  • Jerrybibo
    Jerrybibo almost 8 years
    For some reason I think this is the easiest to comprehend.
  • cjk
    cjk over 7 years
    In case you already have click functions defined on a with href="#" then you can be more specific on the jquery selector, e.g. $('a[href!="#"]')
  • bhollis
    bhollis about 6 years
    Unfortunately, I don't believe that you can include other websites (such as OAuth logins on another domain) in the scope.