简体   繁体   中英

Back button using HTML5 history works only once

I am trying to build a simple javascript-only HTML5-only implementation for the back button in a single page app.

As a first step, I have a skeletal prototype which works, but only once - when I go 1>2>3 and then press back from 3, it comes back to 2 alright, but remains stuck there at 2 no matter how many times more I press the back button. The behavior is always the same, and across browsers, so I am sure it must be my logic, but even after many tries I am unable to figure what's broken. Further, the forward button also seems to be deactivated.

To explain it briefly, I have three (hidden) divs and for each there is a (visible) 'description', clicking on which opens the actual div. When the page loads, only the descriptions are visible and none of the content. When the user clicks on the description of any one div, that div shows, and the rest are/remain hidden.

The site is behind an nginx server which proxy passes everything to the homepage with directives like:

location /div1{
        proxy_pass https://192.168.43.220:8765/;
    }

Since it is relatively simple, and can be directly copy pasted to replicate, I am taking the liberty of pasting it all here. There are already counter variables and many console.logs, so that should make it easier for someone trying to fix it.

I would also appreciate any explanation of why this doesn't work.

<html>
<head>
</head>
<body>
    <b href="#div1" onclick=show_div1()>DIV1</b>
    <b href="#div2" onclick=show_div2()>DIV2</b>
    <b href="#div3" onclick=show_div3()>DIV3</b>

    <div id="div1">
        <p>This is div number 1</p>
    </div>
    <div id="div2">
        <p>This is div number 2</p>
    </div>
    <div id="div3">
        <p>This is div number 3</p>
    </div>
</body>

<script>
    document.addEventListener("DOMContentLoaded", function(event){
        on_page_load();
    });
    function on_page_load(){
        document.getElementById("div1").style.display="none";
        document.getElementById("div2").style.display="none";
        document.getElementById("div3").style.display="none";
        var p = window.location.pathname.split("/")[1];
        console.log("on page load, pathname: " + p);
        history.pushState({url: p}, null, p);
        console.log("js routing to: " + p);
        js_route(p);
        n1 = 0; n2 = 0; n3 = 0;
    }
    function show_div1(){
        document.getElementById("div1").style.display="block";
        document.getElementById("div2").style.display="none";
        document.getElementById("div3").style.display="none";
        n1 = n1 + 1;
        console.log("showing div1 for " + n1 + "th time");
        history.pushState({url: 'div1', n: n1}, null, "div1");
    }
    function show_div2(){
        document.getElementById("div2").style.display="block";
        document.getElementById("div1").style.display="none";
        document.getElementById("div3").style.display="none";
        n2 = n2 + 1;
        console.log("showing div2 for " + n2 + "th time");
        history.pushState({url: 'div2', n: n2}, null, "div2");
    }
    function show_div3(){
        document.getElementById("div3").style.display="block";
        document.getElementById("div2").style.display="none";
        document.getElementById("div1").style.display="none";
        n3 = n3 + 1;
        console.log("showing div3 for " + n3 + "th time");
        history.pushState({url: 'div3', n: n3}, null, "div3");
    }

    window.onpopstate = function(event) {
        var popped_url = event.state.url;
        var popped_n = event.state.n;
        console.log("popstate url: " + popped_url);
        console.log("onpopstate, routing to: " + window.location.pathname + " and n is " + popped_n);
        js_route(popped_url);
        //js_route(window.location.pathname.split("/")[1]);
    }

    function js_route(path){
        switch(path){
            case "div1":
                show_div1();
                break;
            case "div2":
                show_div2();
                break;
            case "div3":
                show_div3();
        }
    }
</script>

Ok, after many more rounds of debugging and contemplation, I figured out what was broken.

The show_div functions all push state into the history. So, when the popstate event handler calls the js_route function, it calls up the correct history, but then pushes that same state back into history. So, when going from 1>2>3, and then back from 3, it goes to 2, but also pushes 2 again into history - so going back again will pop the latest state in history which is now 2. Thus creating an infinite loop on any further popstate events.

The way around this is to create a new set of functions - show_div_hist which do the same thing as the show_div functions, but do not push anything into history. Similarly, a new router js_route_history , which does the same thing as the js_route function but calls up the aforementioned show_div_hist functions instead.

And that's it. Works nicely.

<html>
    <head>
    </head>
    <body>
        <b href="#div1" onclick=show_div1()>DIV1</b>
        <b href="#div2" onclick=show_div2()>DIV2</b>
        <b href="#div3" onclick=show_div3()>DIV3</b>

        <div id="div1">
            <p>This is div number 1</p>
        </div>
        <div id="div2">
            <p>This is div number 2</p>
        </div>
        <div id="div3">
            <p>This is div number 3</p>
        </div>
    </body>

    <script>
        document.addEventListener("DOMContentLoaded", function(event){
            on_page_load();
        });
        function on_page_load(){
            document.getElementById("div1").style.display="none";
            document.getElementById("div2").style.display="none";
            document.getElementById("div3").style.display="none";
            document.getElementById("div4").style.display="none";
            var p = window.location.pathname.split("/")[1];
            console.log("on page load, pathname: " + p);
            history.pushState({url: p}, null, p);
            console.log("js routing to: " + p);
            js_route(p);
            n1 = 0; n2 = 0; n3 = 0;
        }
        function show_div1(){
            document.getElementById("div1").style.display="block";
            document.getElementById("div2").style.display="none";
            document.getElementById("div3").style.display="none";
            document.getElementById("div4").style.display="none";
            n1 = n1 + 1;
            //console.log("showing div1 for " + n1 + "th time");
            history.pushState({url: 'div1', n: n1}, null, "div1");
        }
        function show_div1_hist(){
            document.getElementById("div1").style.display="block";
            document.getElementById("div3").style.display="none";
            document.getElementById("div2").style.display="none";
            document.getElementById("div4").style.display="none";
//          n1 = n1 + 1;
//          console.log("showing div1 for " + n1 + "th time");
//          history.pushState({url: 'div1', n: n1}, null, "div1");
        }

        function show_div2(){
            document.getElementById("div2").style.display="block";
            document.getElementById("div1").style.display="none";
            document.getElementById("div3").style.display="none";
            document.getElementById("div4").style.display="none";
            n2 = n2 + 1;
            //console.log("showing div2 for " + n2 + "th time");
            history.pushState({url: 'div2', n: n2}, null, "div2");
        }
        function show_div2_hist(){
            document.getElementById("div2").style.display="block";
            document.getElementById("div1").style.display="none";
            document.getElementById("div3").style.display="none";
            document.getElementById("div4").style.display="none";
//          n2 = n2 + 1;
//          console.log("showing div2 for " + n2 + "th time");
//          history.pushState({url: 'div2', n: n2}, null, "div2");
        }


        function show_div3(){
            document.getElementById("div3").style.display="block";
            document.getElementById("div2").style.display="none";
            document.getElementById("div4").style.display="none";
            document.getElementById("div1").style.display="none";
            n3 = n3 + 1;
            //console.log("showing div3 for " + n3 + "th time");
            history.pushState({url: 'div3', n: n3}, null, "div3");
        }
        function show_div3_hist(){
            document.getElementById("div3").style.display="block";
            document.getElementById("div2").style.display="none";
            document.getElementById("div1").style.display="none";
            document.getElementById("div4").style.display="none";
//          n3 = n3 + 1;
//          console.log("showing div3 for " + n3 + "th time");
//          history.pushState({url: 'div3', n: n3}, null, "div3");
        }
        window.onpopstate = function(event) {
            event.preventDefault();
            var popped_url = event.state.url;
            var popped_n = event.state.n;
            console.log("popstate url: " + popped_url);
            console.log("onpopstate, routing to: " + window.location.pathname + " and n is " + popped_n);
            js_route_hist(popped_url);
            //js_route(window.location.pathname.split("/")[1]);
        }

        function js_route(path){
            switch(path){
                case "div1":
                    show_div1();
                    break;
                case "div2":
                    show_div2();
                    break;
                case "div3":
                    show_div3();
            }
        }
        function js_route_hist(path){
            switch(path){
                case "div1":
                    show_div1_hist();
                    break;
                case "div2":
                    show_div2_hist();
                    break;
                case "div3":
                    show_div3_hist();
            }
        }

    </script>
</html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM