How to Fix Javascript onbeforeunload not working

Ever had problems with onbeforeunload not triggering? Check out steps to fix this in this post!

Jan 26, 2023 | Read time 10 minutes

🔔 Table of contents

Introduction

Did you ever needed to perform certain action before the user leaves your website. To do this we can use the onbeforeunload event.

Recently, I had to implement this for one of my Vue apps. So when the user closes the page, we handle onbeforeunload to save their temporary data.

This lead me down a rabbit hole of different issues.

This post will go over fixes on why your onbeforeunload event handler is not working. To fix onbeforeunload issues, we can try the following:

  1. Check that you are using the onbeforeunload syntax correctly
  2. Make sure we have returned a value
  3. Check for browser compatibility
  4. Know when to use and the limitations of onbeforeunload

What is the onbeforeunload exactly?

The onbeforeunload event in JavaScript is fired when the document, or a specific element, is about to be unloaded.

When we have attached a handler to this event, a confirmation will open up asking the user if they want to navigate away from the browser!

If you find yourself in a scenario where the onbeforeunload is not loading correctly, consider the below steps to fix that up:

1. Check that you are using the onbeforeunload syntax correctly

One thing that can stump you when trying to get the onbeforeunload event working is that you are not using the syntax correctly.

According to MDN (https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event) we can do in JavaScript like so:

addEventListener('beforeunload', (event) => { 

  event.preventDefault(); /*✔️ To show a dialog we need this preventDefault() */

  //
  // do some action
  //

  return event.returnValue = ''; /*✔️ Need to return a value */

});

We can do it in the following HTML as well:

<body onbeforeunload="handler"/>

<svg onbeforeunload="handler"/>

A few gotchas to keep in mind

There are a few gotchas to keep in mind when working with this beforeunload event though:

  • The following global methods will not work: window.alert(), window.confirm(), and window.prompt()
  • With modern browsers, we cannot change the dialog text that comes up (eg “Leave site - Changes you made may not be saved!")
leave site message in chrome
  • The even will fire if there has been user interaction on the site. If theres no interaction, then this will not fire!

  • If you did not call the preventDefault() on the event, the dialog will not appear. However the code in the event handler will run!

Tip: Mobile unload

To avoid issues with reloading on mobile (from the user accidentally swiping or dragging content - eg google maps, tables), we can use CSS to stop the scroll y behaviour:

    body {
       overscroll-behavior-y: contain !important;
    }

The above just sets the behavior of scroll y to overscroll-behavior-y: contain. This means it will stop scroll chaining which can cause page reloads!

2. Make sure we have returned a value

A key note when handling the onbeforeunload event, is that we have a return value.

So in our event handler for beforeunload, make sure to have the line return event.returnValue = ''

window.addEventListener("beforeunload", (event) => {
  event.preventDefault();
  return event.returnValue = ''; /*✔️ Need this for pseudo elements to work! */
});

The return value and the preventDefault method controls if the dialog will appear. So it may seem that your onbeforeunload is not working, but in fact it is still executing the code in the handler - just not showing the dialog!

3. Check for browser compatibility

Browser support for this has a history of flakyness. Some of the inconsistencies and weirdness that you will need to consider:

  • Different browsers will have different dialog text.

Firefox will use: “This page is asking you to confirm that you want to leave - data you have entered may not be saved.”

Chrome displays the string, “Do you want to leave the site? Changes you made may not be saved.” (see Chrome Platform Status).

  • The browser will show a dialog warning to the user only when event.returnValue has a value. Now to skip the prompt we will have to use null - but for IE we have to use undefined (it will show the “null” text if not used)!

The addEventListener is the preferred way to add a handler to the beforeunload event. However with older browsers (eg IE6, IE7) you can use attachEvent.

function addEvent(object, event_type, event_handler) {
    if (object.addEventListener) {
        object.addEventListener(event_type, event_handler, false);
    } else {
        object.attachEvent("on" + event_type, handler);
    }
}

4. Know when to use and the limitations of onbeforeunload

One of the limitations of beforeunload and unload events is that it does not work well with the browser back buttons (also known as back/forward cache: https://web.dev/bfcache/)

When you have unload/beforeunload event handlers, browsers will not place pages in the bfcache - so could stuff you up if you are somehow catering for the user going back and forth.

Additionally, there are a few quirkyness for when the handler will trigger.

As an example, Pressing F5 to refresh the page counts as a user interaction and kick-of the beforeunload event handler. But if the user clicks the refresh button instead, it does not register as a user interaction and therefore wont fire the event (as of Chrome 81).

From my experience to get around browser issues, its best to register the event handler for onbeforeunload on the <body> HTML tag. Less messing around with adding/ removing handlers or considering using addEventListener vs attachEvent

<body onbeforeunload="handler">

Alternatives for onbeforeunload - using pageshow/ pagehide methods

Now for certain browsers, the behaviour of onbeforeunload can be shaky - especially on Safari, iOS, Firefox.

We can try to use the pageshow/ pagehide events (https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event)

    <html>
    <head>
    <script>

    function pageShown(evt)
    {
        if (evt.persisted)
            alert("pageshow event handler called.  The page was just restored from the Page Cache.");
        else
            alert("pageshow event handler called for the initial load.  This is the same as the load event.");
    }

    function pageHidden(evt)
    {
        if (evt.persisted)
            alert("pagehide event handler called.  The page was suspended and placed into the Page Cache.");
        else
            alert("pagehide event handler called for page destruction.  This is the same as the unload event.");
    }

    window.addEventListener("pageshow", pageShown, false);
    window.addEventListener("pagehide", pageHidden, false);

    </script>
    <body>
    <a href="http://www.webkit.org/">Click for WebKit</a>
    </body>
    </html>

The above code provides more options for us - it allows us to check if the page is being “suspended” (eg the user navigating away) or destroyed (user actually closing the page)

If you want to further make it cross browser, we can do a check support condition like the following:

    if ("onpagehide" in window) {
        window.addEventListener("pageshow", myLoadHandler, false);
        window.addEventListener("pagehide", myUnloadHandler, false);
    } else {
        window.addEventListener("load", myLoadHandler, false);
        window.addEventListener("unload", myUnloadHandler, false);
    }

In the above, we are checking if the browser supports onpagehide - if it does then use the pagehide/ pageshow events. If not, fallback to the unload/load events!

Cancelling the window.onbeforeunload event

We can’t really cancel the the onbeforeunload event due to security reasons. However, we can unregister the event from your HTML element.

So instead of adding the event handler to a global window object like the below:

addEventListener('beforeunload', (event) => {
  // A function that returns `true` if the page has unsaved changes.
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return (event.returnValue = 'Are you sure you want to exit?');
  }
});

Best practice to first create a handler method that we can reference and then add or remove it conditionally:

const beforeUnloadListener = (event) => {
  event.preventDefault();
  return (event.returnValue = 'Are you sure you want to exit?');
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  removeEventListener('beforeunload', beforeUnloadListener);
});

Summary

The onbeforeunload event is great for you to attach a piece of code before the user exits your webpage. In most cases we want to save any unsaved data from the user so that they can continue where they left off later.

However there are some gotchas when dealing with this event - since it can be unreliable. To fix issues of the onbeforeunload not working for our page, we can:

  • check to see if we are using the syntax correctly, using the event.preventDefault and returning event.returnValue
  • Verify that you are not relying on the prompts window.alert(), window.confirm(), and window.prompt() - these will be ignored
  • Check browser compatibility and consider the pagehide/ pageshow events
  • Consider conditionally adding event handlers to the onbeforeunload instead of adding it automattically for performance improvements!

👋 About the Author

G'day! I am Huy a software engineer based in Australia. I have been creating design-centered software for the last 10 years both professionally and as a passion.

My aim to share what I have learnt with you! (and to help me remember 😅)

Follow along on Twitter , GitHub and YouTube