I am using Chrome stable 46.
I have a very basic site. One button.onclick that fetch('a')
and one <a>
tag that opens b
. Both urls a
and b
do not exist, I intend to catch them in the service worker and just return a new Response()
.
If I click on the button which fetches a
, the service worker is not involved and I get a 404 error.
But if I click on the link that goes to b
, the service worker onfetches and returns the response.
Question: Why is the service worker not getting any FetchEvent for the fetch('a')
request?
See the files below
HTML
<html>
<body>
<p><button onclick="fetch('a');">fetch a</button></p>
<p><a href="b">go to b</a></p>
<script src="js.js" defer></script>
</body>
</html>
js.js
navigator.serviceWorker.register('sw.js');
sw.js
self.onfetch = function(event) {
var request = event.request;
event.respondWith(
caches.match(request).then(function(response) {
return new Response('here is your onfetch response');
})
);
};
The service worker's fetch
event handler isn't being called the first time you load the page because the service worker hasn't yet "claimed" the page, meaning it's not under the service worker's control. By default, the first time you visit a site that registers a service worker, the service worker's install
(and potentially activate
) event handlers will run, but fetch
won't get triggered until the service worker takes control. That happens the next time you navigate to a page under the service worker's scope, or if you reload the current page.
You can override this default behavior and have the service worker take control during the initial navigation by calling self.clients.claim()
inside your activate
event handler.
I also want to point out that the fetch
event handler in your sample has some issues—there's no reason to call caches.match(request)
if you're always planning on returning a new Response
object. More importantly, you need to do some sort of check of the event.request.url
value and only return the new Response
if it matches one of your "special" URLs, which in your case is a
and b
. The way things are implemented now, your fetch
handler will return the dummy Response
unconditionally, even if it's a subsequent request for your main page (index.html
). That's presumably not what you want.
I put a gist together that modifies your service worker code to accomplish what I believe you're attempting. You can try out a live version thanks to RawGit. For posterity, here's the modified service worker code:
self.addEventListener('install', event => {
// Bypass the waiting lifecycle stage,
// just in case there's an older version of this SW registration.
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', event => {
// Take control of all pages under this SW's scope immediately,
// instead of waiting for reload/navigation.
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
// In a real app, you'd use a more sophisticated URL check.
if (event.request.url.match(/a|b$/)) {
event.respondWith(new Response('here is your onfetch response'));
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With