Browse Source

Prefetch when visible in the viewport

Alexandre Dieulot 5 years ago
parent
commit
0f9d075fcd
1 changed files with 54 additions and 2 deletions
  1. 54 2
      instantpage.js

+ 54 - 2
instantpage.js

@@ -6,6 +6,7 @@ let lastTouchTimestamp
 
 const prefetcher = document.createElement('link')
 const isSupported = prefetcher.relList && prefetcher.relList.supports && prefetcher.relList.supports('prefetch')
+                    && window.IntersectionObserver && 'isIntersecting' in IntersectionObserverEntry.prototype
 const isDataSaverEnabled = navigator.connection && navigator.connection.saveData
 const allowQueryString = 'instantAllowQueryString' in document.body.dataset
 const allowExternalLinks = 'instantAllowExternalLinks' in document.body.dataset
@@ -14,6 +15,7 @@ const useWhitelist = 'instantWhitelist' in document.body.dataset
 let delayOnHover = 65
 let useMousedown = false
 let useMousedownOnly = false
+let useViewport = false
 if ('instantIntensity' in document.body.dataset) {
   const intensity = document.body.dataset.instantIntensity
 
@@ -23,6 +25,14 @@ if ('instantIntensity' in document.body.dataset) {
       useMousedownOnly = true
     }
   }
+  else if (intensity == 'viewport') {
+    /* Biggest iPhone resolution (which we want): 414 × 896 = 370944
+     * Small 7" tablet resolution (which we don’t want): 600 × 1024 = 614400
+     * Note that the viewport (which we check here) is smaller than the resolution due to the UI’s chrome */
+    if (document.documentElement.clientWidth * document.documentElement.clientHeight < 450000) {
+      useViewport = true
+    }
+  }
   else {
     const milliseconds = parseInt(intensity)
     if (!isNaN(milliseconds)) {
@@ -50,6 +60,40 @@ if (isSupported && !isDataSaverEnabled) {
   else {
     document.addEventListener('mousedown', mousedownListener, eventListenersOptions)
   }
+
+  if (useViewport) {
+    let triggeringFunction
+    if (window.requestIdleCallback) {
+      triggeringFunction = (callback) => {
+        requestIdleCallback(callback, {
+          timeout: 1500,
+        })
+      }
+    }
+    else {
+      triggeringFunction = (callback) => {
+        callback()
+      }
+    }
+
+    triggeringFunction(() => {
+      const intersectionObserver = new IntersectionObserver((entries) => {
+        entries.forEach((entry) => {
+          if (entry.isIntersecting) {
+            const linkElement = entry.target
+            intersectionObserver.unobserve(linkElement)
+            preload(linkElement.href, true)
+          }
+        })
+      })
+
+      document.querySelectorAll('a').forEach((linkElement) => {
+        if (isPreloadable(linkElement)) {
+          intersectionObserver.observe(linkElement)
+        }
+      })
+    })
+  }
 }
 
 function touchstartListener(event) {
@@ -165,8 +209,16 @@ function isPreloadable(linkElement) {
   return true
 }
 
-function preload(url) {
-  prefetcher.href = url
+function preload(url, isFromViewport) {
+  if (!isFromViewport) {
+    prefetcher.href = url
+  }
+  else {
+    const additionalPrefetcher = document.createElement('link')
+    additionalPrefetcher.rel = 'prefetch'
+    additionalPrefetcher.href = url
+    document.head.appendChild(additionalPrefetcher)
+  }
 }
 
 function stopPreloading() {