Coder Perfect

How to Detect the Back Button Event in a Browser – Cross-Browser

Problem

How do you definitively detect whether or not the user has pressed the back button in the browser?

Using a #URL system, how do you enforce the use of an in-page back button inside a single page online application?

Why don’t back buttons in browsers fire their own events?

Asked by Xarus

Solution #1

(Note: I’ve added code to identify backspaces in response to Sharky’s suggestion.)

So, I’ve seen a lot of these questions on SO, and I’ve recently had to deal with the issue of managing back button functionality. I’ve come up with a simple, cross-browser, library-less approach for detecting the back button after a few days of searching for the best solution for my application (Single-Page with Hash Navigation).

Most people recommend using:

window.onhashchange = function() {
 //blah blah blah
}

This function will also be called if a user updates the location hash with an in-page element. When your user clicks, the page goes backwards or forwards, it’s not the finest user experience.

To give you a sense of how my system works, when my user walks through the UI, I’m filling up an array with previous hashes. This is what it looks like:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}

It’s fairly straightforward. This is done to provide cross-browser compatibility and support for outdated browsers. Simply send the new hash to the function, and it will save it and then alter it (which will be saved in the browser’s history).

I also utilize an in-page back button that uses the lasthash array to navigate the user between pages. It appears as follows:

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

As a result, the user will be returned to the last hash, and that hash will be removed from the array (I have no forward button right now).

So. How can I tell if a user has used my in-page back button or the browser’s back button?

I first looked at window.onbeforeunload, but it didn’t help Because that function is only executed when the user is about to switch pages. In a single-page application with hash navigation, this does not happen.

So, after doing some more research, I came across suggestions for attempting to set a flag variable. In my situation, I’d try to set it, but because everything is asynchronous, it wouldn’t always be set in time for the if statement in the hash change, because.onMouseDown wasn’t always called in click, and adding it to a onclick wouldn’t ever trigger it fast enough.

This is when I began to consider the distinction between document and window. My final approach was to use document.onmouseleave to enable the flag and document.onmouseleave to disable it.

My boolean is set to true when the user’s mouse is inside the document area (read: the rendered page, but excluding the browser frame). The boolean switches to false as soon as the mouse exits the document area.

I’ll be able to adjust my window this way. to: onhashchange to: onhashchange to: onhashchange to

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

You’ll notice that #undefined is checked. This is because it returns undefined if there is no history in my array. Using the window.onbeforeunload event, I ask the user if they want to leave.

So, in a nutshell, and for those who aren’t using an on-page back button or an array to save their history:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

That’s all there is to it. In terms of hash navigation, a simple three-part method to detect back button usage vs in-page components.

EDIT:

You may also include the following (thanks to @thetoolman for this question) to verify that the user does not use backspace to initiate the back event:

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});

Answered by Xarus

Solution #2

You can use the popstate event handler, for example:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

Note: To avoid any other unanticipated issues, load this code only on certain pages where you want to implement the logic for the best results.

When the current history entry changes, the popstate event is triggered (user navigates to a new state). This occurs when the user uses the browser’s Back/Forward buttons or when the history.back(), history.forward(), and history.go() functions are called programmatically.

The historical state object is equal to the event.state attribute of the event.

Wrap it around for jQuery syntax (to add even more listeners after the document is ready):

(function($) {
  // Above code here.
})(jQuery);

window.onpopstate on page load is another option.

See also the Single-Page Apps and HTML5 pushState pages for further examples:

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

This should work with Chrome 5+, Firefox 4+, Internet Explorer 10+, Safari 6+, Opera 11.5+, and other comparable browsers.

Answered by kenorb

Solution #3

I’d been battling with this requirement for a while and used some of the above ideas to get it done. However, I made a discovery that appears to operate across Chrome, Firefox, and Safari browsers, as well as Android and iPhone.

On page load:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

Please let me know if this is of assistance. If there’s something I’m missing, please let me know.

Answered by Itzmeygu

Solution #4

In javascript, navigation type 2 indicates that the browser’s back or forward buttons have been used, and the browser is retrieving content from the cache.

if(performance.navigation.type == 2)
{
    //Do your code here
}

Answered by Hasan Badshah

Solution #5

if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

This is the only approach that works for me (although it isn’t a one-page website). Chrome, Firefox, and Safari all support it.

Answered by escanxr

Post is based on https://stackoverflow.com/questions/25806608/how-to-detect-browser-back-button-event-cross-browser