Pure Javascript to detect SPA DOM ready and page change

Viewed 1566

I need to fire a function when the DOM ready or page view changed on single-page applications, here is my code and it works for the first time the user visits the site (DOM ready), but it didn't work when a user switches SPA pages (view changed).

I need a common method for most websites and SPA projects since this code needs to be executed on each of our client's websites(sort of like Google Analytics tracking code), and this code need executed every time when the end-users load or switch the pages. Is there any pure Javascript that can detect when the SPA changes pages?

function docReady(fn) {
    if (document.readyState === "complete" || document.readyState === "interactive") {
        setTimeout(fn, 1);
    } else {
        document.addEventListener("DOMContentLoaded", fn);
    }
}

docReady(function() {
    _AcodeInit();
});

function _AcodeInit()
{
...
}
1 Answers

Option 1: Listening to Changes in URL

As SPAs use routing, you could ideally listen to url changes.

One crude way of doing this is to keep polling using setInterval and track the changes to the url. If the url has changed you could run your handler. However this is wasteful.

Given the lack of options you can listen to all events which could cause transitions, (e.g. mouse clicks, keyboard Enter/Space key press etc.) and in the handler check if the url has changed. Use requestAnimationFrame so that we don't execute handler prematurely.

let currentUrl = location.href;
const checkPageTransition = () => {
    requestAnimationFrame(() => {
        if (currentUrl !== location.href) {
            console.log("url changed");
        }
        currentUrl = location.href;
    }, true);
};


document.body.addEventListener("click", checkPageTransition);
document.body.addEventListener("keyup", e => {
    if (e.code === "Enter" || e.code === "Space") checkPageTransition()
});

Option 2: Listening to popstate event

If you are using modern SPAs, they would most likely be using history.pushState and history.popState to manage routing. We could then listen to the window.popstate event. But there are limitations to this event.

From: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate

Note: Calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.

However we can monkey patch the history.pushState function so that it always fires the history.onpushstate function.

(function(history) { 
    var pushState = history.pushState; 
    history.pushState = function(state) { 
        if (typeof history.onpushstate == "function") 
        { 
            history.onpushstate({ 
                state: state 
            }); 
        } 
        return pushState.apply(history, arguments); 
    } 
})(window.history); 

You would need to similarly patch the replaceState function as well.

Related