javascriptphpajaxhtml5-history

maintain state within div when back or forward button is clicked (html5 pushState)


Regarding on html5 pushState, when back button was clicked, url was able to reflect the changes required but div in-within did not able to show the old info, this also applies to forward button.

I am new to this, below is the codes:

index.php

<button type="button" id="menu-btn-1">Menu 1 (steak)</button>
<button type="button" id="menu-btn-2">Menu 2 (vegan)</button>

<div id="menu-res"></div>

<script src="./index.js" type="text/javascript"></script>

res.php

<?php
if($_POST['order'] == "steak"){
    echo "medium rare steak";
}
if($_POST['order'] == "salad"){
    echo "hawaii salad";
}
?>

index.js

function show(input) {
    var xmlhttp=new XMLHttpRequest();
    xmlhttp.onreadystatechange=function() {
        if (this.readyState==4 && this.status==200) {
            console.log(this.responseText);
            document.getElementById("menu-res").innerHTML=null;
            document.getElementById("menu-res").innerHTML=this.responseText;
            //how to maintain state when back/forward button is clicked?                
            history.pushState(this.responseText, null, input);
        }
    }
    xmlhttp.open("POST","res.php",true);
    xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    let data = 'order='+input;
    xmlhttp.send(data);
}



window.addEventListener('DOMContentLoaded', function() {
    document.getElementById("menu-btn-1").addEventListener('click', ()=> {
        show("steak");
    });
});

window.addEventListener('DOMContentLoaded', function() {
    document.getElementById("menu-btn-2").addEventListener('click', ()=> {
        show("salad");
    });
});

window.addEventListener('popstate', ()=> {
    show("");
});

Solution

  • To use the stored "state" you need to use the popstate listener to render / process whatever data you have stored in the history stack. Taking much from your original code perhaps the following single-page script will help.

    The key to displaying the original content ( which is what I believe was the central point of your issue ) is the use of setdisplay( e.state ) within the popstate listener.

    <?php
        if( $_SERVER['REQUEST_METHOD']=='POST' && isset( $_POST['order'] ) ){
            ob_clean();
            
            switch( $_POST['order'] ){
                case 'steak':
                    echo 'Delicious meaty goodness, cooked to perfection on the grill and served with fries';
                break;
                case 'vegan':
                    echo 'The food my food eats...';
                break;
                case 'protein':
                    echo 'Rather like the Steak menu but greater variety of protein types';
                break;
                case 'carbs':
                    echo 'Energy laden pasta and similar to get you through the day/night';
                break;
                case 'salad':
                    echo 'Rather like the "Vegan" menu but not as fashionable';
                break;
                case 'seafood':
                    echo 'Exquisite delicacies from the World\'s Oceans';
                break;
            }
            
            exit();
        }
    ?>
    <!DOCTYPE html>
    <html lang='en'>
        <head>
            <meta charset='utf-8' />
            <title>History State mangling and manipulation</title>
        </head>
        <body>
        
            <button type='button' data-type='menu' name='steak'>Menu 1 (steak)</button>
            <button type='button' data-type='menu' name='vegan'>Menu 2 (vegan)</button>
            <button type='button' data-type='menu' name='protein'>Menu 3 (High Protein)</button>
            <button type='button' data-type='menu' name='carbs'>Menu 4 (Carbs)</button>
            <button type='button' data-type='menu' name='salad'>Menu 5 (Salad)</button>
            <button type='button' data-type='menu' name='seafood'>Menu 6 (Fish & Seafood)</button>
            
            <div id='menu-res'></div>
            
            <script>
                const setdisplay=( text )=>document.getElementById('menu-res').innerHTML=text;
            
                /* delegated event listener to process button clicks */
                document.addEventListener('click',e=>{
                    if( e.target instanceof HTMLButtonElement && e.target.dataset.type=='menu' ){
                        
                        /* Prepare and send AJAX request */
                        let fd=new FormData();
                            fd.set('order',e.target.name);
                            
                        fetch( location.href,{ method:'post', body:fd } )
                            .then(r=>r.text())
                            .then( text=>{
                                /* Set the history state ... whatever you wish to store within reason. */
                                history.pushState( text, `Menu: ${e.target.name} - ${text}`, '?order='+e.target.name );
                                
                                /* display the ajax response */
                                setdisplay( text );
                            })
                            .catch( alert )
                    }
                });
                
                /* rebuild HTML display when browser back/forward buttons are clicked. */
                window.addEventListener('popstate',e=>{
                    setdisplay( e.state )
                })
            </script>   
        </body>
    </html>