How to Prevent a Page From Being Visited Again by Back Button Javascript
Back/forward enshroud
Optimize your pages for instant loads when using the browser'south back and forward buttons.
— Updated
Dorsum/forward enshroud (or bfcache) is a browser optimization that enables instant back and forrad navigation. It significantly improves the browsing experience for users—peculiarly those with slower networks or devices.
Equally web developers, it'southward critical to empathize how to optimize your pages for bfcache across all browsers, and so your users tin reap the benefits.
Browser compatibility #
bfcache has been supported in both Firefox and Safari for many years, across desktop and mobile.
Starting in version 86, Chrome enabled bfcache for cantankerous-site navigations on Android for a small percent of users. In subsequent releases, boosted support slowly rolled out. Since version 96, bfcache is enabled for all Chrome users across desktop and mobile.
bfcache basics #
bfcache is an in-memory cache that stores a complete snapshot of a page (including the JavaScript heap) equally the user is navigating away. With the entire page in memory, the browser can apace and hands restore it if the user decides to return.
How many times have you visited a website and clicked a link to go to another page, simply to realize it'due south non what you wanted and click the back button? In that moment, bfcache can make a large difference in how fast the previous page loads:
Without bfcache enabled | A new request is initiated to load the previous page, and, depending on how well that folio has been optimized for repeat visits, the browser might have to re-download, re-parse, and re-execute some (or all) of resources information technology merely downloaded. |
With bfcache enabled | Loading the previous folio is essentially instant, because the entire page tin can be restored from retentivity, without having to go to the network at all |
Cheque out this video of bfcache in action to understand the speed upwards it tin can bring to navigations:
In the video above, the case with bfcache is quite a bit faster than the example without it.
bfcache not but speeds upwards navigation, information technology too reduces data usage, since resources do non have to exist downloaded again.
Chrome usage data shows that 1 in 10 navigations on desktop and one in 5 on mobile are either back or forwards. With bfcache enabled, browsers could eliminate the data transfer and fourth dimension spent loading for billions of web pages every single day!
How the "enshroud" works #
The "cache" used past bfcache is dissimilar from the HTTP cache (which is likewise useful in speeding upwards repeat navigations). The bfcache is a snapshot of the unabridged folio in memory (including the JavaScript heap), whereas the HTTP cache contains only the responses for previously made requests. Since information technology'south quite rare that all requests required to load a folio can be fulfilled from the HTTP cache, repeat visits using bfcache restores are always faster than even the well-nigh well-optimized non-bfcache navigations.
Creating a snapshot of a page in memory, however, involves some complication in terms of how best to preserve in-progress code. For example, how do you handle setTimeout()
calls where the timeout is reached while the page is in the bfcache?
The respond is that browsers pause running any pending timers or unresolved promises—essentially all pending tasks in the JavaScript job queues—and resume processing tasks when (or if) the page is restored from the bfcache.
In some cases this is fairly low-risk (for example, timeouts or promises), merely in other cases information technology might lead to very confusing or unexpected behavior. For example, if the browser pauses a job that'south required every bit part of an IndexedDB transaction, it can affect other open tabs in the same origin (since the same IndexedDB databases can exist accessed past multiple tabs simultaneously). Every bit a result, browsers volition generally not attempt to cache pages in the middle of an IndexedDB transaction or using APIs that might affect other pages.
For more details on how various API usage affects a page'south bfcache eligibility, encounter Optimize your pages for bfcache beneath.
APIs to find bfcache #
While bfcache is an optimization that browsers exercise automatically, information technology'due south however important for developers to know when it'south happening then they can optimize their pages for it and conform any metrics or operation measurement accordingly.
The primary events used to observe bfcache are the page transition events—pageshow
and pagehide
—which have been around as long every bit bfcache has and are supported in pretty much all browsers in utilize today.
The newer Page Lifecycle events—freeze
and resume
—are as well dispatched when pages go in or out of the bfcache, as well as in another situations. For case when a background tab gets frozen to minimize CPU usage. Note, the Page Lifecycle events are currently only supported in Chromium-based browsers.
Observe when a page is restored from bfcache #
The pageshow
issue fires right after the load
result when the page is initially loading and whatsoever time the page is restored from bfcache. The pageshow
event has a persisted
property which will be true
if the page was restored from bfcache (and false
if not). You lot tin can use the persisted
property to distinguish regular page loads from bfcache restores. For example:
window. addEventListener ( 'pageshow' , ( event ) => {
if (event.persisted) {
console. log ( 'This page was restored from the bfcache.' ) ;
} else {
panel. log ( 'This page was loaded usually.' ) ;
}
} ) ;
In browsers that support the Page Lifecycle API, the resume
upshot will also fire when pages are restored from bfcache (immediately earlier the pageshow
issue), though information technology will also fire when a user revisits a frozen groundwork tab. If you want to restore a page'southward state later information technology's frozen (which includes pages in the bfcache), y'all can apply the resume
effect, but if you want to measure your site's bfcache hitting rate, you'd need to employ the pageshow
event. In some cases, yous might need to utilize both.
Find when a page is entering bfcache #
The pagehide
event is the counterpart to the pageshow
result. The pageshow
event fires when a page is either loaded unremarkably or restored from the bfcache. The pagehide
event fires when the page is either unloaded normally or when the browser attempts to put it into the bfcache.
The pagehide
outcome as well has a persisted
belongings, and if it'southward false
then you tin be confident a page is non near to enter the bfcache. However, if the persisted
property is true
, it doesn't guarantee that a page volition be buried. Information technology means that the browser intends to enshroud the folio, simply there may be factors that make it impossible to cache.
window. addEventListener ( 'pagehide' , ( event ) => {
if (event.persisted === true ) {
console. log ( 'This folio *might* be entering the bfcache.' ) ;
} else {
console. log ( 'This page will unload normally and be discarded.' ) ;
}
} ) ;
Similarly, the freeze
event volition fire immediately later the pagehide
event (if the consequence's persisted
holding is true
), only again that only means the browser intends to enshroud the folio. It may however have to discard it for a number of reasons explained beneath.
Optimize your pages for bfcache #
Not all pages get stored in bfcache, and even when a folio does get stored there, information technology won't stay there indefinitely. It's disquisitional that developers understand what makes pages eligible (and ineligible) for bfcache to maximize their cache-hit rates.
The following sections outline the best practices to brand it equally probable as possible that the browser can enshroud your pages.
Never apply the unload
effect #
The most important way to optimize for bfcache in all browsers is to never employ the unload
consequence. Ever!
The unload
outcome is problematic for browsers considering it predates bfcache and many pages on the internet operate under the (reasonable) supposition that a folio volition non continue to exist later on the unload
issue has fired. This presents a challenge because many of those pages were also built with the assumption that the unload
consequence would fire any time a user is navigating away, which is no longer true (and hasn't been truthful for a long time).
So browsers are faced with a dilemma, they have to cull between something that can improve the user experience—only might also risk breaking the page.
Chrome and Firefox take called to make pages ineligible for bfcache if they add an unload
listener, which is less risky but besides disqualifies a lot of pages. Safari will endeavour to cache some pages with an unload
event listener, but to reduce potential breakage information technology volition not run the unload
outcome when a user is navigating away, which makes the issue very unreliable.
Instead of using the unload
event, utilise the pagehide
event. The pagehide
event fires in all cases where the unload
event currently fires, and it also fires when a page is put in the bfcache.
In fact, Lighthouse v6.2.0 has added a no-unload-listeners audit
, which will warn developers if whatever JavaScript on their pages (including that from third-party libraries) adds an unload
result listener.
Simply add beforeunload
listeners conditionally #
The beforeunload
event will not make your pages ineligible for bfcache in Chrome or Safari, merely it volition make them ineligible in Firefox, so avert using it unless absolutely necessary.
Different the unload
event, however, at that place are legitimate uses for beforeunload
. For example, when you want to warn the user that they have unsaved changes they'll lose if they leave the page. In this case, it'southward recommended that you only add beforeunload
listeners when a user has unsaved changes then remove them immediately after the unsaved changes are saved.
Avoid window.opener references #
In some browsers (including Chromium-based browsers) if a page was opened using window.open()
or (in Chromium-based browsers prior to version 88) from a link with target=_blank
—without specifying rel="noopener"
—then the opening page will take a reference to the window object of the opened page.
In add-on to being a security hazard, a page with a not-cypher window.opener
reference cannot safely be put into the bfcache considering that could break any pages attempting to access it.
As a result, it's best to avoid creating window.opener
references by using rel="noopener"
whenever possible. If your site requires opening a window and controlling information technology through window.postMessage()
or directly referencing the window object, neither the opened window nor the opener will be eligible for bfcache.
Always close open connections before the user navigates away #
Every bit mentioned above, when a page is put into the bfcache all scheduled JavaScript tasks are paused and so resumed when the folio is taken out of the cache.
If these scheduled JavaScript tasks are only accessing DOM APIs—or other APIs isolated to but the electric current page—and so pausing these tasks while the folio is not visible to the user is not going to cause any problems.
Even so, if these tasks are continued to APIs that are besides accessible from other pages in the same origin (for example: IndexedDB, Web Locks, WebSockets, etc.) this can be problematic considering pausing these tasks may prevent code in other tabs from running.
As a result, some browsers will not attempt to put a page in bfcache in the following scenarios:
- Pages with an open IndexedDB connection
- Pages with in-progress fetch() or XMLHttpRequest
- Pages with an open WebSocket or WebRTC connectedness
If your page is using whatsoever of these APIs, it'due south best to always close connections and remove or disconnect observers during the pagehide
or freeze
event. That will permit the browser to safely cache the page without the risk of it affecting other open up tabs.
Then, if the folio is restored from the bfcache, you can re-open or re-connect to those APIs (in the pageshow
or resume
effect).
The following example shows how to ensure your pages are eligible for bfcache when using IndexedDB by closing an open connection in the pagehide
issue listener:
allow dbPromise;
function openDB ( ) {
if ( !dbPromise) {
dbPromise = new Hope ( ( resolve, decline ) => {
const req = indexedDB. open up ( 'my-db' , 1 ) ;
req. onupgradeneeded = ( ) => req.result. createObjectStore ( 'keyval' ) ;
req. onerror = ( ) => reject (req.error) ;
req. onsuccess = ( ) => resolve (req.result) ;
} ) ;
}
render dbPromise;
} // Close the connexion to the database when the user is leaving.
window. addEventListener ( 'pagehide' , ( ) => {
if (dbPromise) {
dbPromise. so ( db => db. close ( ) ) ;
dbPromise = zero ;
}
} ) ;
// Open the connexion when the page is loaded or restored from bfcache.
window. addEventListener ( 'pageshow' , ( ) => openDB ( ) ) ;
Update dried or sensitive data later bfcache restore #
If your site keeps user state—specially whatsoever sensitive user data—that data needs to exist updated or cleared after a folio is restored from bfcache.
For example, if a user navigates to a checkout page and so updates their shopping cart, a back navigation could potentially surface out-of-engagement information if a stale folio is restored from bfcache.
Some other, more critical example is if a user signs out of a site on a public computer and the adjacent user clicks the back button. This could potentially expose private data that the user assumed was cleared when they logged out.
To avoid situations like this, it'southward skillful to always update the page subsequently a pageshow
event if event.persisted
is true
.
The following code checks for the presence of a site-specific cookie in the pageshow
upshot and reloads if the cookie is non establish:
window. addEventListener ( 'pageshow' , ( consequence ) => {
if (event.persisted && !document.cookie. match ( / my-cookie / ) ) {
// Force a reload if the user has logged out.
location. reload ( ) ;
}
} ) ;
Test to ensure your pages are cacheable #
Chrome DevTools tin help you test your pages to ensure they're optimized for bfcache, and identify whatsoever issues that may be preventing them from beingness eligible.
To test a detail folio, navigate to it in Chrome and and then in DevTools go to Application > Back-forward Cache. Next click the Run Exam button and DevTools will attempt to navigate away and dorsum to determine whether the folio could exist restored from bfcache.
If successful, the panel will written report "Restored from back-forward cache":
If unsuccessful, the panel will indicate the page was non restored and list the reason why. If the reason is something you as a developer can address, that will also be indicated:
In the screenshot higher up, the use of an unload
event listener is preventing the page from being eligible for bfcache. You can fix that past switching from unload
to using pagehide
instead:
How bfcache affects analytics and performance measurement #
If yous track visits to your site with an analytics tool, you volition likely observe a decrease in the total number of pageviews reported as Chrome continues to enable bfcache for more users.
In fact, you're likely already underreporting pageviews from other browsers that implement bfcache since most of the popular analytics libraries practise not track bfcache restores as new pageviews.
If you lot don't want your pageview counts to go down due to Chrome enabling bfcache, yous can written report bfcache restores as pageviews (recommended) by listening to the pageshow
issue and checking the persisted
property.
The post-obit example shows how to exercise this with Google Analytics; the logic should be similar for other analytics tools:
// Transport a pageview when the page is showtime loaded.
gtag ( 'event' , 'page_view' ) ; window. addEventListener ( 'pageshow' , ( event ) => {
if (event.persisted === true ) {
// Send another pageview if the page is restored from bfcache.
gtag ( 'event' , 'page_view' ) ;
}
} ) ;
Performance measurement #
bfcache tin also negatively impact functioning metrics nerveless in the field, specifically metrics that measure page load times.
Since bfcache navigations restore an existing folio rather than initiate a new page load, the full number of page loads collected will decrease when bfcache is enabled. What'due south disquisitional, though, is that the folio loads being replaced by bfcache restores would likely take been some of the fastest page loads in your dataset. This is because back and forwards navigations, by definition, are repeat visits, and repeat page loads are generally faster than page loads from first time visitors (due to HTTP caching, equally mentioned before).
The outcome is fewer fast page loads in your dataset, which will likely skew the distribution slower—despite the fact that the performance experienced past the user has probably improved!
At that place are a few means to deal with this issue. One is to annotate all page load metrics with their respective navigation type: navigate
, reload
, back_forward
, or prerender
. This will allow you lot to proceed to monitor your performance inside these navigation types—even if the overall distribution skews negative. This approach is recommended for non-user-centric page load metrics like Fourth dimension to Starting time Byte (TTFB).
For user-centric metrics like the Cadre Web Vitals, a better option is to study a value that more accurately represents what the user experiences.
Impact on Core Spider web Vitals #
Cadre Web Vitals measure the user'south experience of a web page across a variety of dimensions (loading speed, interactivity, visual stability), and since users experience bfcache restores as faster navigations than traditional folio loads, it'southward of import that the Core Web Vitals metrics reflect this. Afterwards all, a user doesn't care whether or not bfcache was enabled, they just intendance that the navigation was fast!
Tools like the Chrome User Feel Report, that collect and report on the Core Web Vitals metrics treat bfcache restores as split page visits in their dataset.
And while at that place aren't (still) defended web performance APIs for measuring these metrics after bfcache restores, their values can be approximated using existing spider web APIs.
- For Largest Contentful Paint (LCP), y'all can use the delta between the
pageshow
upshot's timestamp and the timestamp of the next painted frame (since all elements in the frame will be painted at the same fourth dimension). Notation that in the example of a bfcache restore, LCP and FCP will exist the same. - For First Input Delay (FID), you can re-add the event listeners (the same ones used by the FID polyfill) in the
pageshow
consequence, and report FID as the filibuster of the first input after the bfcache restore. - For Cumulative Layout Shift (CLS), you tin can keep to keep using your existing Operation Observer; all you take to do is reset the current CLS value to 0.
For more than details on how bfcache affects each metric, refer to the individual Core Spider web Vitals metric guides pages. And for a specific example of how to implement bfcache versions of these metrics in lawmaking, refer to the PR adding them to the web-vitals JS library.
Boosted Resources #
- Firefox Caching (bfcache in Firefox)
- Page Cache (bfcache in Safari)
- Back/forward cache: spider web exposed behavior (bfcache differences across browsers)
- bfcache tester (test how different APIs and events touch on bfcache in browsers)
Last updated: — Improve commodity
Render to all articles
Source: https://web.dev/bfcache/
0 Response to "How to Prevent a Page From Being Visited Again by Back Button Javascript"
Post a Comment