Search code examples
jqueryajaxbrowser-history

content loads in div via ajax, hash appended to url, but can't add history and bookmark state


I'm ashamed to say how much time I've spent struggling with this, trying various methods I've found here and elsewhere, and I'm desperately in need of a little help. Here's where I'm at: I have a series of interactive SVG charts that contain various links (to WP posts), each assigned the post-link class. On click, the content of any linked post successfully loads via ajax in a div beneath the chart, and a hash fragment is appended to the URL. All good so far.

But I just can't manage to create and capture history in a way that allows browser back button functionality or capture the 'ajaxed' state of the page to allow for bookmarks or link sharing.

I've researched getting browser history with pushState, replaceState, and popstate, thinking that would be the solution, but nothing I tried worked. So I've removed those incorrect placements and provided the basic code below, seeking a little guidance to get the title, back button and bookmarks functioning. The code below is included within a larger document ready function:

//AJAX LOAD SINGLE POST CONTENT ON PAGE
$(function(){

    //enable accordion to function within ajaxed content
    $.ajaxSetup({
        cache:false,
        complete: function() {
            var cpicons = {
                header: "iconClosed",
                activeHeader: "iconOpen"
            };
            $(".accordion").accordion({
                header: "h3",
                collapsible: true,
                heightStyle: "content",
                navigation: true,
                icons:cpicons
            });
            $(".accordion-allclosed").accordion({
                header: "h3",
                collapsible: true,
                active: true,
                heightStyle: "content",
                navigation: true ,
                icons: cpicons
            });
        }
    });

    $(".post-link").click(function(e) {
        var toLoad = $(this).attr("href")+"#single-jobtype-post-container";         

        //capture slug from post url to use as hash
        var slug = $(this).attr("href").match(/[^/]*(?=(\/)?$)/)[0];
        window.location.hash = slug;
        //insert spinner followed by post's content 
        $("#single-jobtype-post-container").html("<div class='loading d-all t-all m-all'><i class='fas fa-spinner fa-spin fa-3x'></i></div>");
        $("#single-jobtype-post-container").load(toLoad);

        e.preventDefault();             
    });     

});

--- UPDATE ---

As suggested by Kamil below, I flipped my approach and let the URL drive the JavaScript via the hashchange event. Much simpler, problem solved, and lesson learned! Here's my working code:

        $(function() {

        $("#exploration-grid a").click(function(e) {
            window.location.hash = $(this).attr("id");
            e.preventDefault();

            //set the "active" class as appropriate
            $('#exploration-grid a').removeClass('active');
            $(this).addClass('active');  

        });

        $(window).bind("hashchange", function(){
            var newHash = window.location.hash.substring(1);
            var postURL = window.location.protocol + "//" + window.location.host + "/" + "jobtypes" + "/" + newHash;

            if (newHash) {

                $("#single-jobtype-post-container").html("<div class='loading d-all t-all m-all'><i class='fas fa-spinner fa-spin fa-3x'></i></div>");
                $("#single-jobtype-post-container").load(postURL);

                var hashval = newHash.replace(/-/g, " ");
                hashtitle = hashval.toUpperCase();
                document.title = document.title + " - " + hashtitle;

            };

        });

        $(window).trigger("hashchange");

    });

Solution

  • How about instead of changing the hash after ajax'ing, you change the hash first, and then react to the hash change by loading the appropriate content?

    Consider the following post: How to implement a hash-key navigation?

    The relevant takeaway from the post is the following:

    if ("onhashchange" in window) {  
      alert("The browser supports the hashchange event!");  
    }  
    
    function locationHashChanged() {  
      if (location.hash === "#somecoolfeature") {  
        somecoolfeature();  
      }  
    }  
    
    window.onhashchange = locationHashChanged
    

    You will want to stick the content loading code in the locationHashChanged function and download the snippet based on the hash. The browser should automatically keep track of the hashes in its history, and you only need to put in reactionary code for each state.

    I've worked on an ERP site that used Hash navigation to support a single page model. It was fairly cool stuff to me when I started out web dev.