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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Blackcoat
  • 3,280
  • 30
  • 25

5 Answers5

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
 }
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.

Jacob
  • 77,566
  • 24
  • 149
  • 228
  • 1
    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? – Blackcoat Dec 14 '10 at 17:37
  • 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. – Jacob Dec 14 '10 at 18:05
  • Where exactly one sets these variables? – YakovL May 29 '16 at 09:53
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
jball
  • 24,791
  • 9
  • 70
  • 92
  • 1
    I wouldn't depend on the server and client to be synced particularly well :/ – Matchu Dec 13 '10 at 21:50
  • 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. – Yahel Dec 13 '10 at 21:50
  • @Matchu, the synching is a tricky problem without generating another call to the server to time the delay. – jball Dec 13 '10 at 21:56
  • 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? – Blackcoat Dec 13 '10 at 22:00
  • @Blackcoat - you might be able to do something similar with cookies... I'll see if I can come up with an example. – jball Dec 13 '10 at 22:39
  • 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) – Blackcoat Dec 13 '10 at 22:50
0

good answer: https://stackoverflow.com/a/9870920/466363

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

Community
  • 1
  • 1
Shimon Doodkin
  • 4,310
  • 34
  • 37
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
Christian Joudrey
  • 3,441
  • 25
  • 25
  • 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. – Blackcoat Dec 14 '10 at 17:35