I was working on a Pen recently when I accidentally clicked away… and the browser let me! Uhhhh, that’s not good — that’s lost work. We were using a handler like this to prevent this:
window.onbeforeunload = function() {
if (shouldWarnYouLogic()) {
return "You have unsaved changes!";
}
}Code language: JavaScript (javascript)
It just… stopped working in Chrome 119. So, in case you do something similar, be warned!
You need to know three things…
- You have to
preventDefault()on the event. - You can’t return an empty string (even though the browser doesn’t do anything with it anyway).
- You have to instantiate this event after user interaction.
Updated code:
window.addEventListener('click', warnAboutLostChanges);
function bindWarnAboutLostChanges() {
window.addEventListener(
'beforeunload',
function(e) {
if (shouldWarnYouLogic()) {
e.preventDefault();
return "You have unsaved changes!";
}
}
);
window.removeEventListener('click', warnAboutLostChanges);
}Code language: JavaScript (javascript)
Fortunately, I noticed this, and we shipped the fix before we heard from anyone about losing anything. 😅
Thoughts?
Email me or comment below.
Also CodePen PRO is quite a deal. 🙏
thanks man, i faced same challenge too
I don’t think the first requirement (preventDefault) is a requirement at all, though it is best practice as noted on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
Check out: https://www.tunetheweb.com/experiments/beforeunload/
As long as you click on the page it reliably fires and blocks the unload with a pop up despite the lack of preventDefault.
The second is a requirement (non-empty string), as you could do a check in the before unload handler and return a falsey value, which would then allow the page to unload without the pop up. Browsers USED to show the message passed but since this looks like browser UI, it became a phishing vector so was removed from all browsers. Here’s the Chrome bug for that: https://bugs.chromium.org/p/chromium/issues/detail?id=587940. At this point we could have changed it from a string to a boolean, but that would have broken the web so a string that isn’t really used is how it remains as a historical artifact.
The third (needs a user interaction here) I think it is the main issue here but is incorrectly stated. You do NOT need to do a browser interaction before adding the event, BUT the event (whether added before or after an interaction) is ignored if no browser action happens before it is fired. Again use the above test page to confirm this.
A click anywhere on the page counts as a user interaction so doing that, or navigating via the link, causes the pop up to show. Editing the URL bar without interacting with the page causes the pop up NOT to show. It can be easy to not do this correctly in testing and assume no interaction happened (when it did) or vice versa. This was added in Chrome 60 (from way back in 2017): https://chromestatus.com/feature/5082396709879808 and was already in Firefox by and added to Safari shortly afterwards.
So these last two requirements are true for all browsers (Chromium-based browsers, Firefox, and Safari). Safari actually is even more unreliable as if a page is eligible for the bfcache, then it won’t fire this even with a page interaction :-( I made the example page have a
cache-control: no-storeheader to prevent it being eligible so this example page acts the same in all three browsers.AFAIK nothing changed on this in Chrome 119 and, as I say above, Chrome’s usage is consistent in all other browsers (though Safari ignores it even more often) and has been for some time. But happy to be corrected here if I’ve missed something.
Thanks so much for all the details Barry!
119.0.6045.159 = This is true.
119.0.6045.123 = Not true. See video: https://share.cleanshot.com/6Z5vLyYB
(Yes, that’s Arc, but the behavior was exactly the same in my stable Chrome when it matched that exact version before it updated)
All I know is what we tested as a team together. When we registered the handler before an interaction, it didn’t work, when we changed it to registering after a click, it worked. This is so vitally important we had to go with what worked.
Very weird!
I can’t reproduce this on Chrome 119.0.6045.123:
https://www.tunetheweb.com/before-unload.mp4
I tried it on ARC but unfortunately I’m already on 119.0.6045.159 there and, as you stated, it does work fine there.
I wonder was this a bug in ARC? Or something else going on? Good that it’s at least fixed in the latest version though.
Either way I can definitely tell you it’s not the way it’s supposed to work! And also you shouldn’t need to register the event first NOR use preventDefault AFAIK (though best practice to do that).
Very weird indeed! I would be quick to blame Arc just since it’s third-party and who knows what all they do. But we were replicating this on Stable Chrome so ♀️. Anyway just a blip I guess.
I have the opposite issue. I don’t want the “Are you Sure” popups, and as of today (119.0.6045.160) preventing them no longer seems possible.
handleTabClose = event => {
event.preventDefault();
So it looks like I shouldn’t have been using preventDefault( ) at all. But what’s odd is that i wasn’t getting the “are you sure” popups before, and now (as of the latest update) I am. Is it possible that google added some default messages, that didn’t previously exist? So if you didn’t explicitly provide one as in your example, that it was effectively a noop? And now, you get the pre-canned “are you sure” message?
Turns out this was indeed a bug in Chrome:
https://issues.chromium.org/issues/40586353#comment11
Fixed in 119 and (and rolled out through Finch so initially some 119 instances might not have had it)
MDN was updated a month before this post to note this, but unfortunately we all missed that:
https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event