Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use prefetch for "instant" page loads #68

Open
dbackeus opened this issue Jul 15, 2020 · 0 comments
Open

Use prefetch for "instant" page loads #68

dbackeus opened this issue Jul 15, 2020 · 0 comments
Labels
enhancement A general improvement

Comments

@dbackeus
Copy link
Collaborator

dbackeus commented Jul 15, 2020

Something that could be considered to improve cross page navigation on public pages is to employ a prefetching strategy. Prefetching implies preloading other pages that a client might visit in the future, populating the browser in-memory cache so that there is no request needed when the client actually navigates to the cached page. There are different approaches to prefetching, but one approach I find particularily attactive is to prefetch when a client is hovering a link long enough to suggest that there is intention to click.

The first library I'm aware of that published an implementation for this approach was instantclick which later on gave birth to a leaner library called instant.page.

Neither of these are compatible with TurboLinks out of the box though and there are related open issues on both the Turbolinks repo (turbolinks/turbolinks#313) and instant.page repo (instantpage/instant.page#52).

On the Swedish Sahaja Yoga website which is built using Wordpress I managed to get prefetching + turbolinks working based on a script published on Rails developer Magnus Skog's blog (https://www.mskog.com/posts/instant-page-loads-with-turbolinks-and-prefetch/). It leverages the hoverintent library together with a few lines of JavaScript. The entire implementation can be summed up with:

<script defer src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.min.js"></script>
<script async src="https://cdn.jsdelivr.net/npm/hoverintent@2.2.1/dist/hoverintent.min.js"></script>
<script>
  document.addEventListener("turbolinks:load", setupPrefetch)
	
  function setupPrefetch() {
    // Since we're loading hoverintent using async it may not be available on the first turbolinks:load
    // event so we keep trying every 50ms until the hoverintent function is available.
    if (typeof hoverintent == "undefined") {
      setTimeout(setupPrefetch, 50)
      return
    }
	  
    var hoverIntentOptions = {
      interval: 50,
      sensitivity: 5
    }

    document.querySelectorAll("a").forEach(node => {
      if (node.dataset.turbolinks === "false") return
      
      var prefetcher
      hoverintent(
        node,
        function() {
          var href = this.getAttribute("href")
          // In Magnus Skog's original script he only applies prefetching to links have a relative href
          // (ie. starting with `/`). However since Wordpress uses absolute URL's I added the second 
          // conditional which would also match hrefs beginning with https://sahajayoga.se.
          if (!href.match(/^\//) && !href.lastIndexOf(location.origin, 0) == 0) return

          if (prefetcher) {
            if (prefetcher.getAttribute("href") != href) {
              prefetcher.getAttribute("href", href)
            }
          } else {
            var link = document.createElement("link")
            link.setAttribute("rel", "prefetch")
            link.setAttribute("href", href)
            prefetcher = document.body.appendChild(link)
          }
        },
        function() {}
      ).options(hoverIntentOptions)
    })
  }
</script>

A limitation of the hover approach to prefetch is that it won't really work well on touch screen devices though I believe it's possible to start prefetching on touchstart (equivalent to mousedown when using a mouse) which will make the loading processing start earlier than usual (normally would start on touchend / mouseup). Since the space between start and end is often around 100ms it can still be a noticable gain.

Another limitation with the script pasted above is that it won't actually do anything in Safari (desktop or mobile) since it's based on "prefetch hints" which is not yet supported in Safari.

@dbackeus dbackeus added the enhancement A general improvement label Jul 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A general improvement
Projects
None yet
Development

No branches or pull requests

1 participant