Logo for The Department of Better Technology.

Rewiring Government

The Department of Better Technology helps governments deliver great digital services to the people who depend on them.

Using Turbolinks to hack JavaScript's beforeunload

Screendoor logo

Streamline the process behind your online forms.

Learn about Screendoor →

Want to jump straight to the JavaScript library? https://github.com/ajb/beforeunload.js

If you’ve ever implemented auto-save behavior in a web app, you’re probably familiar with JavaScript’s beforeunload event, which is the “hook” that lets you stop a user before they leave a page. This is what it looks like in Chrome:

beforeunload alert

Most applications implement auto-save on a timer – you wouldn’t want to hit the server every time a user types a new letter. A simple version of this behavior might look something like this:

// Check and save changes every 3 seconds
setInterval(function(){
  if (hasChanges) {
    saveChanges();
  }
}, 3000);

// Alert user if they try to exit with unsaved changes
$(window).bind('beforeunload', function(){
  if (hasChanges) {
    return 'You have unsaved changes!';
  }
});

Can you spot the problem with this code? If the user navigates to another page before the 3-second interval has triggered, they’ll see the big, ugly beforeunload warning:

annotated screenshot

Thankfully, my coworker Josh identified this problem, and posed the question:

github issue

As I thought about potential solutions, I realized that we already had another tool at our disposal, although one that might seem unrelated: Turbolinks. When a user on a modern browser clicks an internal link in Screendoor, Turbolinks hijacks the native browser behavior and uses HTML5 pushState to load the new content.

In fact, since Turbolinks already hijacks this event, it was pretty simple to add a call to saveChanges() before the page is changed:

$(document).on('page:before-change', function(e){
  saveChanges({
    callback: function(){
      Turbolinks.visit(e.originalEvent.data.url);
    });
  });
});

With this approach, most users never see the beforeunload alert at all – if they attempt to navigate away from a page that has unsaved changes, we simply save those changes and then load the new page.

It’s always fun to find a perfect use for a library that its author might not have envisioned, especially when it results in a cleaner experience for our users. We’ve even wrapped this behavior up with an easy-to-use API if it’s useful to others: https://github.com/ajb/beforeunload.js

To user experience, Turbolinks, and getting rid of pesky alerts!

:beers: :computer: :ok_hand:

Adam Becker is a co-founder of The Department of Better Technology.

Want more articles like this? Subscribe to our newsletter.