0

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");

    });
robg
  • 3
  • 2
  • do you want use ajax to be able to undo redo? – plonknimbuzz Mar 01 '18 at 08:58
  • you can use back forward button on browser if you are using non ajax (native) submit. but if you use ajax submit you cant use back/forward button on browser. the problem is, you want your ajax to have undo(back) or redo(forward) feature, right? – plonknimbuzz Mar 01 '18 at 09:00
  • @plonknimbuzz I'm not sure what you mean by undo/redo, but to clarify, I just need to be able to come back to the page (whether by browser history, bookmark or shared link) and have it load the same state based on the URL. And since I want each unique page state to correspond with a unique URL, I've changed the URL with a hash event associated with each content load. From what I've read about using AJAX, I think I need to update the history manually using history.pushState, then create a function testing for a popstate event and update the content as required. But I'm not sure how to do that. – robg Mar 01 '18 at 12:18

2 Answers2

0

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.

Kamil Jarosz
  • 361
  • 1
  • 10
  • Thank you very much for this suggestion! It put me on the right track, and confirmed a suspicion that I might be served by shifting to let the URL drive the js via the hashchange event. With that approach, I got everything working as desired. I'll post my working code beneath my question for others. Sidenote: As if this hashchange technique wasn't outdated enough, I also loaded Ben Alman's "jQuery hashchange event" plugin -- not needed for my code to work, but just to support dinosaur browsers. ;-) Thanks! – robg Mar 02 '18 at 01:16
0

I don't know why you are doing a flow like this, but if you want, you can try this script:

a.html

<!doctype html>
<html>
<head>
    <meta charset="utf-8"><!--needed to define charset if you want grab using load() correctly-->
</head>
<body>
<a class="post-link" href="http://localhost/gauz/b.html">try me (B)<a><br>
<a class="post-link" href="http://localhost/gauz/c.html">try me (C)<a><br>
<div id='single-jobtype-post-container'>I'm default for a.html</div>
<div class="share-link">share link: <span></span></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(function(){

    function getData(url, clicked=false){
        if(window.location.hash == ''){
            //capture slug from post url to use as hash
            var slug = url.match(/[^/]*(?=(\/)?$)/)[0];
            window.location.hash = slug.match(/[^/]*(?=(\/)?$)/)[0];
        }else{
            if(clicked) window.location.hash = url.match(/[^/]*(?=(\/)?$)/)[0];
            var a = url.split('/');
            a.splice(-1,1, window.location.hash.replace('#',''));
            var url = a.join('/');
        }

        //var toLoad = url+"#single-jobtype-post-container"; wrong, you need space here to just grab content inside id=single-jobtype-post-container
        var toLoad = url+" #single-jobtype-post-container"; //see the space?

        //set shared link
        $('.share-link').find('span').text(window.location.href);
        //insert spinner followed by post's content 
        $("#single-jobtype-post-container").html("loading");
        $("#single-jobtype-post-container").load(toLoad);
    }

    $(".post-link").click(function(e) {
        getData($(this).attr("href"), true);
        e.preventDefault();             
    });

    //first time load
    if(window.location.hash != '')  
        getData(window.location.href);

    //we need hashchange event to handle back and forward history button
    $(window).on('hashchange', function( e ) {
        getData(window.location.href);
    });
});
</script>
</body>
</html>

b.html (example other page)

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<div id="a">I am b.html</div>
<div id="b">xxxxxx</div>
<div id="single-jobtype-post-container">I'm your target of B</div>
<div id="c">yyyyy</div>
</body>
</html>

c.html (another example)

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<div id="a">I am c.html</div>
<div id="b">xxxxxx</div>
<div id="single-jobtype-post-container">I'm your target of C</div>
<div id="c">yyyyy</div>
</body>
</html>
halfer
  • 19,824
  • 17
  • 99
  • 186
plonknimbuzz
  • 2,594
  • 2
  • 19
  • 31
  • Thank you for this info and code. Although I wasn't able to get it to successfully support browser history, it did give me some insight that I might be able to be to use in the future. Thanks! – robg Mar 02 '18 at 01:19