简体   繁体   中英

AJAX page load/history.pushState not working properly after navigating off website

I have a website that uses XMLHttpRequests to request parts of webpages when the user clicks a link.

Relevant code:

function requestPage(url, push) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            main.innerHTML = xhr.responseText;
            attachLinkClickHandlers(main); // `main` = container element

            if (push)
                window.history.pushState(url, title, url);
        }
    };

    xhr.open('GET', url);
    xhr.setRequestHeader('Content-Only', 1); // Server responds to Content-Only header by not sending everything surrounding the main content.
    xhr.send();
}

window.addEventListener('popstate', function (e) {
    var page = e.state;
    requestPage(page, false);
});

window.history.replaceState(location.href, document.title, location.href);
attachLinkClickHandlers(document);

On link click:

requestPage(link.href, true);

Example server responses:

Normal request response:

<!doctype html>
<html>
    <head>
        ...stuff...
    </head>
    <body>
        ...stuff...
        <main>
            <h1>Content</h1>
            <p>Content Content</p>
        </main>
        ...stuff...
    </body>
</html>

Response with Content-Only header set:

<h1>Content</h1>
<p>Content Content</p>

Everything works as intended (page loading, back/forward button navigation within the site), except ... If I click on an external link (to which the click handlers are not attached, because that would be cross origin) and then press the back button on the browser, I'm greeted with what would be the response of the XHR - simply the content, no <html> , <head> , or <body> tags, which means no CSS or JavaScript either.

How can I make the browser load the full page when this happens?

Are you using the same URL to retrieve two different types of content?

If so I had exactly the same problem. I was using the same URL and differing request headers to return either the html application page or some JSON data. After a history.pushState call and navigation away clicking back would result in the JSON response being displayed in the browser (as this was the last response the browser cache associated with the URL).

It appears the browser only keeps track of URLs and their responses. Using XHR request headers to specify which type of content you actually desire is not something the browser keeps track of. So when you navigate away and then return all the browser has is the URL (and possibly a cached response).

My fix was to alter the URLs that actually fetched the JSON data from those used to modify the browser history.

// on click of product item 123
var url = 'http://<webserver>/products/123',
    title = 'Product 123';

history.pushState({
        url: url,
        title: title
    }, title, url);

product = $.getJSON(url + '?json=1'); // make the url different

I put together a some little tests to highlight how this works https://github.com/robbishop/history-api-test

I have tried this in a few browsers and note that Firefox (version 50.0) works differently to Chrome. For example, if you modify the page content and then call history.pushState with a fake URL, then navigate away, the click back. In Chrome you get a 404 page not found (as the URL is not real), but in Firefox it actually restores the page as you last saw it. So some browser differences too, but I think associating URLs with a single content type is probably safer when directly manipulating browser histor

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