48

Looks like Apple has disabled the window.onbeforeunload event for iOS devices (iPhone, iPad, iPod Touch). Unfortunately I can't find any documentation as to why this event doesn't work in Mobile Safari.

Does anyone know if there's a reliable alternative to this function? Android's browser appears to support it just fine, and the Safari desktop application also supports the onbeforeunload event without issue.

Jim Blackler
  • 22,946
  • 12
  • 85
  • 101
8three
  • 661
  • 1
  • 5
  • 10
  • This is a duplicate of [window.onbeforeunload not working on the iPad?](http://stackoverflow.com/questions/3239834/window-onbeforeunload-not-working-on-the-ipad) – Peter V. Mørch Aug 07 '13 at 12:06
  • Does this answer your question? [window.onbeforeunload not working on the iPad?](https://stackoverflow.com/questions/3239834/window-onbeforeunload-not-working-on-the-ipad) – miken32 Dec 21 '19 at 01:00

4 Answers4

20

I see that it's an old question, but i faced this problem recently.

I'm using window.unload and it works fine in ios browsers (although if you look at Apple documentation it seems to be deprecated and they recommend to use document.pagehide)

Nick De Beer
  • 5,232
  • 6
  • 35
  • 50
Miquel
  • 8,339
  • 11
  • 59
  • 82
2

If you really need it, you cant just get all links, forms and DOM objects that have a handler changing the url and make those wait until you've done what you want. For the links, you get them by getElementsByTagName, check if the href starts with anything but a # and just add your onbeforeunload function add onclick (which will be invoked before the href is looked at). Same for the forms but with onsubmit. And finaly, for the elements changing the href with JavaScript, you should make sure when you add the lsitener that you call your onbeforeunlaod function (or, if you use DOM0 or DOM1 listeners, you can just add some class and then use a global script that checks all elements with the class and adds it to the event listener with a closure.

But you should normaly be able to avoid the use of this event (probably using cookies to store the thing you wanted to send every x seconds and allowing to, in the worst case, have a look at it next time the user loads a page and, in the best case, be able to send an Ajax request at onbeforeunload or onunload which, even if it sends only the http headers, woudl allow you to get what you want).

xavierm02
  • 8,457
  • 1
  • 20
  • 24
  • Thanks Xavier, all possibilities, but these all require a lot of DOM modification which in the case of my application is impractical as it might interfere with the functioning of the hosted pages. – Jim Blackler Apr 30 '11 at 06:37
  • You don't need to add the event on every single link and so on, you can use event delegation. You can just put one on the body and use e.srcElement || e.target and then check if it'll change the url. This way, you don't need to put that many elements. But you might still need to put the onsubmit on the forms themselfves... not sure about that. – xavierm02 Apr 30 '11 at 10:04
  • 1
    Any options for the scenario the user is actually closing the page, as opposed to moving to another page via an element? – Edwin Daniels Aug 11 '14 at 20:46
  • Nope. If it were on a computer, you could detect the mouse getting out of the window and get ready for it. The best would probably be to tell the server " I'm still here" all the time so that it can deduce that you've left when you don't, so putting a server timeout. It might be better to use websockets for that instead of many Ajax calls. Other than that, the only thing I can think of is always force the user too have something focused an send the message at onblur ( and maybe add a little while true after it). Anyway, nothing you can use without annoying users. – xavierm02 Aug 11 '14 at 22:08
2

Based on Xavier's answer, I devised a solution along these lines:

function doStuff() {
  // here goes your logic
}

function isSafariMobile() {
  return navigator && /Safari/.test(navigator.userAgent) && /iPhone|iPad/.test(navigator.userAgent)
}

function addWatcherToLinks(baseNode) {
  if (!baseNode || !baseNode.querySelectorAll) { return; } // ignore comments, text, etc.
  for (const link of baseNode.querySelectorAll("a")) {
    link.addEventListener('click', doStuff);
  }
  for (const form of baseNode.querySelectorAll("form")) {
    form.addEventListener('submit', doStuff);
  }
}

// ...when the page loads...
// we watch the page for beforeunload to call doStuff
// Since Safari mobile does not support this, we attach a listener (watcher) to each link and form and then call doStuff.
// Also, we add such a watcher to all new incoming nodes (DOMNodeInserted).
if (isSafariMobile()) {
  addWatcherToLinks(document);
  window.addEventListener("DOMNodeInserted", (event) => { addWatcherToLinks(event.target); }, false);
} else {
  window.addEventListener('beforeunload', doStuff);
}

This solution has some limitations. The biggest one is that it attaches itself to all forms and all links. Sometimes this might not be desired. If you need it you can skip some nodes (e.g. mark them with a particular data- attribute).

Motine
  • 1,638
  • 18
  • 18
0

I was having the same problem. it seems safari browser in iphone triggers only focus and blur events and almost every other event is not triggered, e.g.(pagehide, pageshow, visibility change) but the good news is focus and blur event are supported and triggered on iphone, ipad & android mobiles as well.

    window.addEventListener('focus', function(){
       // do stuff
     });

   window.addEventListener('blur', function(){
       // do stuff
     });

hope this helps anyone.

ikramf90
  • 92
  • 2
  • 17