Skip to content

Instantly share code, notes, and snippets.

@vitobotta
Created January 14, 2021 22:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vitobotta/dc264d271fa07363622b7c2cd9352d5a to your computer and use it in GitHub Desktop.
Save vitobotta/dc264d271fa07363622b7c2cd9352d5a to your computer and use it in GitHub Desktop.
Prefetching
let lastTouchTimestamp
const prefetches = new Set()
const prefetchElement = document.createElement('link')
const isSupported = prefetchElement.relList && prefetchElement.relList.supports && prefetchElement.relList.supports('prefetch')
&& window.IntersectionObserver && 'isIntersecting' in IntersectionObserverEntry.prototype
const allowQueryString = true //'instantAllowQueryString' in document.body.dataset
const allowExternalLinks = false // 'instantAllowExternalLinks' in document.body.dataset
const useWhitelist = false //'instantWhitelist' in document.body.dataset
const mousedownShortcut = false //'instantMousedownShortcut' in document.body.dataset
const DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION = 1111
let delayOnHover = 65
const eventListenersOptions = {
capture: true,
passive: true,
}
if (isSupported) {
document.addEventListener('touchstart', touchstartListener, eventListenersOptions)
document.addEventListener('mouseover', mouseoverListener, eventListenersOptions)
}
function touchstartListener(event) {
/* Chrome on Android calls mouseover before touchcancel so `lastTouchTimestamp`
* must be assigned on touchstart to be measured on mouseover. */
lastTouchTimestamp = performance.now()
const linkElement = event.target.closest('a')
if (!isPreloadable(linkElement)) {
return
}
preload(linkElement.href)
}
function mouseoverListener(event) {
if (performance.now() - lastTouchTimestamp < DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION) {
return
}
const linkElement = event.target.closest('a')
if (!isPreloadable(linkElement)) {
return
}
linkElement.addEventListener('mouseout', mouseoutListener, {passive: true})
mouseoverTimer = setTimeout(() => {
preload(linkElement.getAttribute("href"))
mouseoverTimer = undefined
}, delayOnHover)
}
function mouseoutListener(event) {
if (event.relatedTarget && event.target.closest('a') == event.relatedTarget.closest('a')) {
return
}
if (mouseoverTimer) {
clearTimeout(mouseoverTimer)
mouseoverTimer = undefined
}
}
function isPreloadable(linkElement) {
if (!linkElement || !linkElement.href || linkElement.dataset.turbo == "false") {
return
}
if (useWhitelist && !('instant' in linkElement.dataset)) {
return
}
if (!allowExternalLinks && linkElement.origin != location.origin && !('instant' in linkElement.dataset)) {
return
}
if (!['http:', 'https:'].includes(linkElement.protocol)) {
return
}
if (linkElement.protocol == 'http:' && location.protocol == 'https:') {
return
}
if (!allowQueryString && linkElement.search && !('instant' in linkElement.dataset)) {
return
}
if (linkElement.hash && linkElement.pathname + linkElement.search == location.pathname + location.search) {
return
}
if ('noInstant' in linkElement.dataset) {
return
}
return true
}
function preload(url) {
if (prefetches.has(url)) {
return
}
const prefetcher = document.createElement('link')
prefetcher.rel = 'prefetch'
prefetcher.href = url
document.head.appendChild(prefetcher)
prefetches.add(url)
}
document.addEventListener("turbo:load", () => {
const currentUrl = new URL(location.href).pathname
prefetches.forEach((url) => {
if (url != currentUrl) {
prefetches.delete(url)
}
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment