Is My Page Being Loaded from the Browser Cache?
Asked Answered
S

5

16

I have a "new items" badge on a page that I want to update immediately the page is loaded from the cache (i.e. when hitting "Back" or "Forward" to return to this page). What is the best way to accomplish this?

The setup is pretty simple. The layout for the app looks for new items every 8 seconds, and updates the badge + list of items accordingly.

$(function() {
    setInterval( App.pollForNewItems, 8000 );
});

When someone navigates away from this page to look at the details of an item, a lot can happen. Things are "new" until any user has viewed them, and the app will likely have several user using it simultaneously (the kind of workflow used for a call center or support tickets).

To make sure that the badges are always up to date, I have:

$(window).bind('focus load', function ( event ) {
    App.pollForNewItems();
});

..And though this works, polling for new items on 'load' is only useful when the page is loaded from the cache. Is there a reliable cross-browser way to tell if a page is being loaded from the cache?

Stablish answered 13/12, 2010 at 21:44 Comment(0)
D
7

A partial hacky solution is to have a var with the current time set on the server, and set a var with the current client time at the top of the page. If they differ by more than a certain threshold (1 minute?) then you could assume it's a cached page load.

Example JS (using ASP.Net syntax for the server side):

var serverTime = new Date('<%= DateTime.Now.ToUniversalTime().ToString() %>');
var pageStartTime = Date.UTC(new Date());
var isCached = serverTime < pageStartTime &&
               pageStartTime.getTime() - serverTime.getTime() > 60000;

Alternatively, using cookies on the client side (assuming cookies are enabled), you can check for a cookie with a unique key for the current version of the page. If none exists, you write a cookie for it, and on any other page access, the existence of the cookie shows you that it's being loaded from the cache.

E.g. (assumes some cookie helper functions are available)

var uniqueKey = '<%= SomeUniqueValueGenerator() %>';
var currentCookie = getCookie(uniqueKey);
var isCached = currentCookie !== null;
setCookie(uniqueKey); //cookies should be set to expire 
                      //in some reasonable timeframe
Directed answered 13/12, 2010 at 21:48 Comment(6)
I wouldn't depend on the server and client to be synced particularly well :/Grados
The problem with that is that you're assuming synchronization of clocks. Time in JS is determined by the local computer's time, which may differ significantly from the server's time.Ontology
@Matchu, the synching is a tricky problem without generating another call to the server to time the delay.Directed
Interesting! Though this would actually work for my situation, I'm still wondering if there is a cross-browser solution for figuring out if a page was actually loaded from the cache. Is that even possible in JS?Stablish
@Stablish - you might be able to do something similar with cookies... I'll see if I can come up with an example.Directed
True, though that could hurt performance more than the current solution. I was worried about the overhead of the unnecessary $(window).bind('load') handler when the cache is not used. Extra cookies will be sent with every HTTP request to the domain, so I'd like to avoid them if I can. But you're right, they will accomplish the task of telling us what we need to know (same with other forms of local storage, though they aren't cross-browser)Stablish
K
16

Navigation Timing is in most browsers now(ie9+) http://www.w3.org/TR/navigation-timing/#sec-navigation-info-interface

 if (!!window.performance && window.performance.navigation.type === 2) {
   // page has been hit using back or forward buttons
 } else {
   // regular page hit
 }
Krill answered 7/1, 2015 at 19:36 Comment(3)
this solution seems not to work in Firefox? Is there a workaround for Firefox too?Sapper
best answer for my problem!!Arundel
@Sapper It is working for me in Firefox 61 on macOS 10.13Statolith
C
8

You can ask the web browser to not cache the page. Try these HTTP headers:

Cache-control: no-cache
Cache-control: no-store
Pragma: no-cache
Expires: 0

Particularly, Cache-control: no-store is interesting because it tells the browser to not store the page in memory at all which prevents a stale page being loaded when you hit the back/forward button.

If you do this instead, you don't have to poll for data on page load.

Cambist answered 14/12, 2010 at 2:36 Comment(3)
I didn't even think about caching headers! This is probably the right approach as long as performance doesn't take a hit (I'd want to make sure it doesn't force the UA to always download shared assets that should be cached across the app, e.g. JS, CSS, images). Though this is a solution for the problem, I'm still curious about whether or not you can detect if a page is loaded from the cache via JS. Any thoughts?Stablish
I don't know if there's a way to tell if the page was from cache or not. @jball has the right idea for detecting the date of when the page was rendered, though.Cambist
Where exactly one sets these variables?Valid
D
7

A partial hacky solution is to have a var with the current time set on the server, and set a var with the current client time at the top of the page. If they differ by more than a certain threshold (1 minute?) then you could assume it's a cached page load.

Example JS (using ASP.Net syntax for the server side):

var serverTime = new Date('<%= DateTime.Now.ToUniversalTime().ToString() %>');
var pageStartTime = Date.UTC(new Date());
var isCached = serverTime < pageStartTime &&
               pageStartTime.getTime() - serverTime.getTime() > 60000;

Alternatively, using cookies on the client side (assuming cookies are enabled), you can check for a cookie with a unique key for the current version of the page. If none exists, you write a cookie for it, and on any other page access, the existence of the cookie shows you that it's being loaded from the cache.

E.g. (assumes some cookie helper functions are available)

var uniqueKey = '<%= SomeUniqueValueGenerator() %>';
var currentCookie = getCookie(uniqueKey);
var isCached = currentCookie !== null;
setCookie(uniqueKey); //cookies should be set to expire 
                      //in some reasonable timeframe
Directed answered 13/12, 2010 at 21:48 Comment(6)
I wouldn't depend on the server and client to be synced particularly well :/Grados
The problem with that is that you're assuming synchronization of clocks. Time in JS is determined by the local computer's time, which may differ significantly from the server's time.Ontology
@Matchu, the synching is a tricky problem without generating another call to the server to time the delay.Directed
Interesting! Though this would actually work for my situation, I'm still wondering if there is a cross-browser solution for figuring out if a page was actually loaded from the cache. Is that even possible in JS?Stablish
@Stablish - you might be able to do something similar with cookies... I'll see if I can come up with an example.Directed
True, though that could hurt performance more than the current solution. I was worried about the overhead of the unnecessary $(window).bind('load') handler when the cache is not used. Extra cookies will be sent with every HTTP request to the domain, so I'd like to avoid them if I can. But you're right, they will accomplish the task of telling us what we need to know (same with other forms of local storage, though they aren't cross-browser)Stablish
S
0

Personally, I would set data attribute containing the item id for each element.

I.e.

<ul>
  <li data-item-id="123">Some item.</li>
  <li data-item-id="122">Some other item.</li>
  <li data-item-id="121">Another one..</li>
</ul>

Your App.pollForNewItems function would grab the data-item-id attribute of the first element (if newest are first) and send it to the server with your original request.

The server would then only return the items WHERE id > ... which you can then prepend them to the list.

I'm still confused as to why you want to know if the browser has a cached version of the page.

Also, is there a reason for binding to load instead of ready?

  • Christian
Scope answered 14/12, 2010 at 2:29 Comment(1)
Christian, good suggestion, though the application already does exactly what you describe (though timestamps are compared instead of IDs). As for using 'load' instead of 'ready', I want to also bind to the 'focus' event, and I want to keep my code DRY. 'Ready' is an artificial event constructed by jQuery, and it can only be bound to the document. If you try $(document).bind('focus ready', { ... }), the handler will never be fired for the 'focus' event. Since the time difference between the 'ready' and 'load' events should be negligible for pages loaded from cache, 'load' is just fine here.Stablish
P
0

good answer: https://mcmap.net/q/67366/-detecting-browser-cache-by-jquery

You could also use Navigation Timing to measure the network latency in great detail.

Here is a good article: http://www.html5rocks.com/en/tutorials/webperformance/basics/

If the time difference between fetchStart and responseStart is very low, the page was loaded from cache, for example.

by stewe

Portulaca answered 28/10, 2015 at 15:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.