Hoppe Hoppe - 2 months ago 6
Javascript Question

How to replace the location hash and only keep the last history entry?

I'm using the jQuery BBQ plug-in to track the users progress through the page. However, I only want to create 1 additional entry in the user's history, not one for every hash change.

I've tried the

jQuery.bbq.pushState
and
merge_mode
methods, without success: New history entries are still added:

jQuery.bbq.pushState({ sort: encodeURIComponent(sort) });


I have also tried
location.replace()
, but that doesn't work for Safari 5.1.2.

location.replace('#' + encodeURIComponent(sort))


What's the cross-browser solution to modify the hash, without adding too much entries to the history?

Answer

First, I show the definition of function replaceHash, which accepts only one argument: The new location hash. A detailed explanation of the logic can be found at the bottom of the answer.

Code:

// Should be executed BEFORE any hash change has occurred.
(function(namespace) { // Closure to protect local variable "var hash"
    if ('replaceState' in history) { // Yay, supported!
        namespace.replaceHash = function(newhash) {
            if ((''+newhash).charAt(0) !== '#') newhash = '#' + newhash;
            history.replaceState('', '', newhash);
        }
    } else {
        var hash = location.hash;
        namespace.replaceHash = function(newhash) {
            if (location.hash !== hash) history.back();
            location.hash = newhash;
        };
    }
})(window);
// This function can be namespaced. In this example, we define it on window:
window.replaceHash('Newhashvariable');

Function logic

  • When history.replaceState is supported, the function will always replace the current hash, without any side effects.
  • Otherwise, a reference (hash) to the very first location.hash property is created, and the following function is defined:

    1. If location.hash != hash, then we know for sure that the history's state is at least past the first page view. We can safely go back in the history, without unloading the page. history.back(); // Go back in the history.
    2. Then, set the location.hash property. If we went back in the history in the previous step, the history entry is overwritten.


    The fallback (last) method may not always replace the history:
    When location.hash == hash, either of the following is true:

    1. The hash hasn't changed yet, so it makes no sense to go back to the previous page.
    2. It's possible that the user navigated backwards, to the original page's state. If we use history.back();, the page might be unloaded, which is not desirable.


    So, to be safe, we never unload the page when the hash is equal to the saved original hash.

    Note: It is important to run this code before a hash change. When the hash has already been changed, the script is not reliable any more. The user could have navigated to the very first hash state, which is not equal to the saved hash. Consequently, history.back() unloads the page.