Search code examples
javascriptjqueryhtmlbrowser-history

Manipulating Browser History for AJAX loaded content


I have a page with navigation links on the left and a content div on the right. The navigation links HTML are set up like this.

<a href="javascript:;" class="nav-link" data-mode="myModeValue">Test</a>

I have set up a JavaScript function as seen below that I thought would bind to the links, push a new state into the browser history and load the content for that link based on the data-mode value. Going FORWARD works perfectly, however when I go backward sometimes I have to click back twice to get go back to the previous hash and I'm not sure why.

Here is what I have so far.

<script language="javascript">
    // Create var indicating the CURRENT mode we're on - home being the default when page loads
    currentMode = "home"; 
    // Push the default page (mode) into the history
    history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + currentMode);

    // bind to my nav links
    $(document).on('click', '.nav-link', function(e){
        e.preventDefault();
        ajaxLoadVehicleSearchDetailContent($(this).data('mode'));
    });

    function ajaxLoadVehicleSearchDetailContent(mode) {
        // Get the mode and link object
        var thisMode = mode.toString().toLowerCase();
        var thisLink = $('a[data-mode="' + mode + '"]');
        // If we're switching to a different tab - continue
        if (currentMode != thisMode) {
            currentMode = thisMode;     
            // Get the content via AJAX
            $.ajax({
                url: "/myAjaxFunctionFileURL",
                type: "POST",
                data: { method:"getTabContent", mode:thisMode },
                success: function(result) {
                    history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + thisMode);
                    // Update the content area - fadeOut,replace,fadeIn
                    $('#contentArea').fadeOut(globalAnimationDuration, function() {
                        $('#contentArea').html(result).fadeIn(globalAnimationDuration);
                        $('.nav-link').removeClass('current');  
                        thisLink.addClass('current');                   
                    });
                }
            });     
        }
    }

    // Listen for the popstate and load correct content when user goes BACK in history
    window.addEventListener("popstate", function() {
        if (location.hash.indexOf("#!") != -1) {
            var thisMode = location.hash.split("/").slice(-1)[0];           
            ajaxLoadVehicleSearchDetailContent(thisMode);           
        }
    }, false);
</script>

The globalAnimationDuration var is simply a variable with a value of 500.

Could anyone shine some light onto why the strange behavior when going back? Or even a good example showing how to perform the task that I could follow and update my methods accordingly?

JSFiddle Here

Thank you!


Solution

  • I figured it out. Since I'm calling the same ajaxLoadVehicleSearchDetailContent on both forward and back navigation, it was adding items to the stack errorneously when clicking back. So it would pop the items from the stack, call the function which then re-added it to the stack unless it was the current tab.

    My solution added an additional argument to my ajaxLoadVehicleSearchDetailContent() method as seen below.

    function ajaxLoadVehicleSearchDetailContent(mode,backward) {
            // Get the mode and link object
            var thisMode = mode.toString().toLowerCase();
            var thisLink = $('a[data-mode="' + mode + '"]');
            // If we're switching to a different tab - continue
            if (currentMode != thisMode) {
                currentMode = thisMode;     
                // Get the content via AJAX
                $.ajax({
                    url: "/myAjaxFunctionFileURL",
                    type: "POST",
                    data: { method:"getTabContent", mode:thisMode },
                    success: function(result) {
                        if (!backward) history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + thisMode);
                        // Update the content area - fadeOut,replace,fadeIn
                        $('#contentArea').fadeOut(globalAnimationDuration, function() {
                            $('#contentArea').html(result).fadeIn(globalAnimationDuration);
                            $('.nav-link').removeClass('current');  
                            thisLink.addClass('current');                   
                        });
                    }
                });     
            }
        }
    

    Then when I call it from my nav link - send false, and when I call it from my popstate event I call true.