I’m in Halifax, NS, Canada Flag of Canada  |  It’s 3° at 4:47am

Getting Titular

This post is over 3 years old. Some information may be out-of-date!

Most of the time I just screw around with things on this site, trying to figure out new technology and generally just breaking stuff that no one really looks at. It’s good and helpful and makes me better at what I do. Problem is, I never write about what I learn. It’s the same old story: sure, I’ve been doing this for 20+ years but I’m a dummy and no one wants to read about the lessons of a dummy. It makes perfect sense in my head don’t ya know. This post is an attempt to not be like that sometimes, maybe.

You see, lately I’ve been learning about this web technology called Service Workers. They’re more exciting and interesting than they sound, trust me. One of the things these Service Workers let us web smashers do is create a better offline experience. What’s this? Offline web? How can this be? It’s true and it’s amazing. The web relies on networks to link from one place to another but these networks can be unreliable. From tunnels during a commute, to power outages, to a cabin in the mountains, networks come and go. Our enjoyment of the web comes and goes with them. This is where our new Service Workers come in. Fist bump emoji.

As usual I start off learning new things by reading about what others have done and shamelessly stealing some of their shit. One of the people I have shamelessly stolen shit from is Jeremy Keith who tinkers much like I do, but he’s not a dummy. He recently wrote a book called Going Offline which is perfectly descriptive. I like to buy books written by people from whom I’ve stolen ideas which means I have a large library. In this most excellent — and quick to read I might add — book Mr Keith tells stories of offline adventures and shows in simple, clear code how you can have offline adventures too. At the end he says — and I’m paraphrasing here — go forth and make things and tell everyone about it. So here we are.

Now, about those titles#now,-about-those-titles

Near the end of the book there are some examples on how to create a more engaging offline page, one which lists the articles a visitor has umm, visited on your site and provide a link to revisit them while offline. What a great idea! I should do that! There are others doing this sort of thing and I had previously copied their ideas for pulling article titles and descriptions from a JSON feed or some such thing. While it worked, it was a data hog, pulling all my articles in a giant mass of JSON when my service worker was installed, caching them, etc. I won’t tell you how many kilobytes were harmed in that madness, I’ll just say it was way too many. What could I do? Where could I get the article titles and descriptions?

And then it dawned on me. “Good grief” I said to myself but with swears. “Good grief, all the data you want is already cached ya dummy”. See, when navigating my site, the service worker caches a copy of the article. This helps with site speed but it also helps when offline. When offline the article just loads like there is nothing wrong with the network! I could use this to my advantage on my offline page by whipping up a script that opens the saved article cache, loops over the articles, tries to fetch them and fails from the network but succeeds from the cache because we’re offline.

With the copy of the article from the cache, I can pull the title and description from the article itself. With the article HTML taken from the cache, I created a new DOMParser instance and accessed the title and description by querying the DOM with good old vanilla Javascript. Just like that, I had the information I needed from data I already cached. No new requests, no crazy JSON in the cache, just being smart with what was already there.

Here is the code that I just inline on the bottom of my offline page:


// first open the articles cache
// this cache is specified in the service worker where the
// offline handling code is kept
caches.open('articles')
  .then(articlesCache => {
    articlesCache.keys()
      .then(keys => {
        // loop over each article from the cache
        keys.forEach(request => {
          let markup = '';
          let articleTitle = '';
          let articleDescription = '';

          // fetch the article, this will fail (we're offline)
          // and return the cached article
          fetch(request)
            .then(response => {
              let contentType = response.headers.get('content-type');
              if (contentType.includes('text/html')) {
                return response.text();
              }
            })
            .then(text => {
              // fire up a new DOMParser, we'll use it to parse the returned HTML article
              const parser = new DOMParser();
              const htmlDocument = parser.parseFromString(text, "text/html");

              // heyyyooo, use the DOM to get the title of the article
              articleTitle = htmlDocument.documentElement.querySelector("title").innerText;

              // here one could maybe strip out stuff from the title string one
              // doesn't want to display

              // we can also get the article description from the document,
              // it is after all, THE ARTICLE DOCUMENT
              articleDescription = htmlDocument.documentElement.querySelector('meta[name="description"]').getAttribute('content');

              // one could pull from the parsed HTML document anything they wish
              // at this point. got images cached? get one of 'em to display
              // with the title and description.

              // now it's time to output the title and description
              // the list item will go into a ul in the offline template
              const articleListItem = document.createElement("li");
              const articleListItemLink = document.createElement("a");

              // just checking if the article title is blank (it shouldn't be)
              // and if it is just jam the link into the list of offline articles
              if (articleTitle == '') {
                articleListItemLink.href = request.url;
                articleListItemLink.innerText = request.url;
                articleListItem.appendChild(articleListItemLink);
              }
              else {
                // cool, we have a title, so let's make a slightly more attractive
                // article/description block
                // one could make whatever markup/layout one wanted here
                articleListItemLink.href = request.url;
                articleListItemLink.innerText = articleTitle;
                articleListItem.appendChild(articleListItemLink);
                const articleListItemBreak1 = document.createElement("br");
                articleListItem.appendChild(articleListItemBreak1);
                const articleListItemDescription = document.createTextNode(articleDescription);
                articleListItem.appendChild(articleListItemDescription);
              }

              // now append the offline article to the list in the offline page template
              document.getElementById('savedpostlist').appendChild(articleListItem);
            })
            .catch( () => {
              console.log("Error!");
            });
        });
      });
  });

It’s also available in a Github Gist. It seems to work pretty well around these parts, but as all things online, your mileage may vary.

§