Skip to content

Instantly share code, notes, and snippets.

@hopsoft
Last active December 27, 2023 02:45
Show Gist options
  • Save hopsoft/ab500a3b584e2878c83137cb539abb32 to your computer and use it in GitHub Desktop.
Save hopsoft/ab500a3b584e2878c83137cb539abb32 to your computer and use it in GitHub Desktop.
Turbolinks Prefetching
const hoverTime = 400
const fetchers = {}
const doc = document.implementation.createHTMLDocument('prefetch')
function fetchPage (url, success) {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.setRequestHeader('VND.PREFETCH', 'true')
xhr.setRequestHeader('Accept', 'text/html')
xhr.onreadystatechange = () => {
if (xhr.readyState !== XMLHttpRequest.DONE) return
if (xhr.status !== 200) return
success(xhr.responseText)
}
xhr.send()
}
function prefetchTurbolink (url) {
fetchPage(url, responseText => {
doc.open()
doc.write(responseText)
doc.close()
const snapshot = Turbolinks.Snapshot.fromHTMLElement(doc.documentElement)
Turbolinks.controller.cache.put(url, snapshot)
})
}
function prefetch (url) {
if (prefetched(url)) return
prefetchTurbolink(url)
}
function prefetched (url) {
return location.href === url || Turbolinks.controller.cache.has(url)
}
function prefetching (url) {
return !!fetchers[url]
}
function cleanup (event) {
const element = event.target
clearTimeout(fetchers[element.href])
element.removeEventListener('mouseleave', cleanup)
}
document.addEventListener('mouseover', event => {
if (!event.target.dataset.prefetch) return
const url = event.target.href
if (prefetched(url)) return
if (prefetching(url)) return
cleanup(event)
event.target.addEventListener('mouseleave', cleanup)
fetchers[url] = setTimeout(() => prefetch(url), hoverTime)
})
@frederikhors
Copy link

let can be changed to const on lines 6, 22, 48.

'url' is never reassigned. Use 'const' instead.eslint(prefer-const)

@feliperaul
Copy link

@hopsoft Can you please consider adding xhr.setRequestHeader('Accept', 'text/html') to avoid triggering format.js responses on controllers that use respond_to do |block| syntax, since it's we're always looking for the HTML versions?

That fixed the ActionController::InvalidCrossOriginRequest I was getting on that controller/action.

@hopsoft
Copy link
Author

hopsoft commented Mar 20, 2020

Thanks for suggesting this. I'll get the gist updated.

@vitobotta
Copy link

Hi, thanks a lot for this gist! I've tried adding the touchstart event too for mobile but it doesn't seem to work. Any suggestions? Thanks!

@antulik
Copy link

antulik commented Jun 8, 2020

Cleanup event does not clear cancelled requests. To fix it use this method

function cleanup (event) {
  const element = event.target
  clearTimeout(fetchers[element.href])
  delete fetchers[element.href]
  element.removeEventListener('mouseleave', cleanup)
}

@marcus-at-localhost
Copy link

Could someone check my class of these functions? I'm not sure if I did everything right there...

https://gist.github.com/marcus-at-localhost/3e694ffa7ca226e20964908292601b3b

@antulik
Copy link

antulik commented Sep 16, 2021

This no longer works with Turbo 7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment