For anyone who doesn't know, bfcache is the Back/Forward cache in browsers. I recently fixed an unusual issue in a page that I host that was caused by this.
For multiple years, this site ran fine until suddenly it started misbehaving. Navigating back to a page after following a link would occasionally just reload the current page. Originally I thought this was something wrong with my service worker cache. However, after careful analysis, I discovered the true root of the issue, bfcache.
When a page is restored from bfcache, it is NOT rebuilt using DOMParser
. The page is loaded back to the same state at which you left it including any modifications to the DOM and even the console log history is maintained.
In this page I have animations that run when a link is clicked. This flow eventually leads to a tranistionend
event that triggers the actual page navigation. Essentially like this, though a good deal more complex with a chain reaction of animations.
button.addEventListener('click', () => {
layout.addEventListener('transitionend', navigateAway);
requestAnimationFrame(transitionOut);
});
function transitionOut() {
layout.classList.remove('in');
}
function navigateAway() {
location.href = button.url;
}
The issue I was having was that the transitionend
event handler was remaining active on page restore, and thus, when the transition IN animations ended after returning to a cached page, that would trigger an immediate location change as if the button was just clicked again. This transitionend
handler was lazily not cleaned up since the expectation was for the page to be unloaded immediately after it fired. Properly calling to removeEventListener
before the actual navigation cleared the issue right up.
function navigateAway() {
layout.removeEventListener('transitionend', navigateAway);
location.href = button.url;
}
Now this is a pretty specific and unusual case, but if you are noticing unexpected behavior in your web apps after page navigation, bfcache restoring your page instead of it reloading may be at the source of it. To confirm if bfcache is causing your troubles, it can be disabled by registering an unload
event handler.
window.addEventListener('unload', () => {});
The event handler doesn't need to do anything, just exist. On desktop browsers this makes the page ineligible for bfcache. This is not reliable on mobile browsers and, of course, disabling bfcache is not the right approach to fix some of the other quirks I was seeing. I had to dig deeper.
What I found is that, use of the unload
and beforeunload
(which I was using for some cleanup) events are no longer recommended and the best way to address this was by handling any state cleanup inside a pagehide
event handler and set everything back up in a pageshow
event handler.
window.addEventListener('pagehide', event => {
console.log('saving to bfcache, perform state saving here');
});
window.addEventListener('pageshow', event => {
if (event.persisted) {
console.log('restored from bfcache, restore state here');
} else {
console.log('normal page load, do fresh page setup');
}
});
For the best user experience, the above approach is recommended. However, if you absolutely MUST start with a fresh page load for whatever reason. This can be accomplished by triggering a reload after a pageshow
event.
window.addEventListener('pageshow', event => {
if (event.persisted) { location.reload(); }
});
Top comments (0)