Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent Chrome from loading a cached webpage when offline?

Steps to reproduce the issue:

  1. The user visits the webpage, see the code below.
  2. The user closes Chrome.
  3. The device goes completely offline (turn all networking off manually).
  4. The user re-opens the browser while completely offline.
  5. Chrome automatically serves the last visited page, a saved copy of the webpage which says Online? true, even after hitting refresh several times.

The only thing that tells the user that she/he is looking at some stale, completely unusable copy of the web page is this in the address bar:

enter image description here

Non-technical users can easily miss that, and are left wondering why the page is unusable... This is bad user experience.

Browser & device: Chrome 81 on Android 6 on an Acer Iconia Tab 10 A3-40 tablet.

The webpage is served over HTTPS (secure connection).

Code:

const setMsg = (flag) => {
  const p = document.getElementById('msg')
  p.innerHTML = '<b>Online?</b>  ' + flag
}

setMsg(navigator.onLine)

window.addEventListener("online", () => {
  setMsg(true)
})
window.addEventListener("offline", () => {
  setMsg(false)
})
<p id='msg'> </p>

As far as I can tell:

  • Chrome does not re-run any JavaScript in Step 5, even after hitting refresh.
  • Chrome does not respect the Cache-Control: private, no-store either; double-checked.

So far, the only way I could prevent this from happening is to register a service worker. When I have a service worker registered, the JavaScript is re-run and I can properly and clearly inform the user that she/he is offline.

Without a service worker, how can I prevent Chrome from loading a stale, unusable webpage when offline?

The usual "No internet" page with the dinosaur is appropriate, and that's what I was expecting with Cache-Control: no-store.

like image 801
Ali Avatar asked Apr 20 '20 15:04

Ali


People also ask

How do I permanently disable cache in Chrome?

Here's how... When you're in Google Chrome, click on View, then select Developer, then Developer Tools. Alternatively, you can right click on a page in Chrome, then click Inspect. Click on the Network tab, then check the box to Disable cache.

Can I save a WebPage to view offline?

You can save webpages to read later, even if you're offline, like when you're on an airplane or somewhere else without an Internet connection. To read webpages later offline, download them in Chrome ahead of time.


1 Answers

A very inelegant workaround is to use a CSS animation that makes an "offline" element appear all at once after a set amount of time. With JavaScript, you can continually reset the animation, preventing it from ever showing unless/until JavaScript is no longer running to reset it.

The animation in the example below is set to 3 seconds, and uses keyframes to stay at opacity:0 until 99% of the animation duration, at which point it changes to opacity:1.

In JavaScript we can reset the animation every 1-2 seconds with JavaScript by removing the class that is associated with it, forcing a reflow (by fetching the offsetWidth, for instance), and then adding the animation class back.

Ideally we could tell Chrome on these mobile devices to not cache the page at all. The various headers we tried in the comments on the question were not respected by a mobile device running with no Internet access.

Disabling the caching would be a more appropriate solution in both concept and execution. Having a running JavaScript interval that is perpetually forcing a reflow isn't great. But, it does give the user an indication that the JavaScript engine has stopped running, which is what the situation is here.

In the example below, the "Simulate stop JS" button just clears the interval that is continuing to reset the loading animation. This is a simulation only, but has the same effect as JavaScript not running (tested on an isolated server).

const overlay = document.getElementById('offline-overlay');

const stopJS =  document.getElementById('stopJS');

const heartbeat = () => {
  overlay.classList.remove("animate-overlay");
  void overlay.offsetWidth; 
  overlay.classList.add("animate-overlay");
}

const heartbeatInterval = setInterval( heartbeat, 1000);

stopJS.addEventListener("click", function(){
  clearInterval(heartbeatInterval);
});
@keyframes offline {
  0%   {opacity:0;}
  99%  {opacity:0;}
  100% {opacity:1;}
}

.animate-overlay{
  animation-name: offline;
  animation-duration: 3s;
}

#offline-overlay{
  background-color: rgba(255,255,255,.9);
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  display:flex;
  justify-content: center;
  align-items:center;
  pointer-events:none;
}

#offline-overlay span{
  font-size:300%;
  font-family: sans-serif;
  letter-spacing:5px;
  color: #888;
}
  <div id="offline-overlay" class="animate-overlay">
    <span>OFFLINE</span>
  </div>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam varius quam sed nulla feugiat varius. Praesent vitae mi et libero porttitor maximus. Suspendisse eu pulvinar quam. Phasellus id ante a elit faucibus cursus. Curabitur porttitor vehicula ornare. Suspendisse nec risus ex. Aenean bibendum auctor ex eget aliquet. Donec laoreet sem ut tortor viverra aliquam.</p>

<button id="stopJS">Simulate Stop JS</button>
like image 63
tmdesigned Avatar answered Sep 28 '22 18:09

tmdesigned