Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@frederikhors frederikhors commented Sep 26, 2019

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

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

@feliperaul

This comment has been minimized.

Copy link

@feliperaul feliperaul commented Mar 18, 2020

@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

This comment has been minimized.

Copy link
Owner Author

@hopsoft hopsoft commented Mar 20, 2020

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

@vitobotta

This comment has been minimized.

Copy link

@vitobotta vitobotta commented Mar 27, 2020

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

This comment has been minimized.

Copy link

@antulik 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

This comment has been minimized.

Copy link

@marcus-at-localhost marcus-at-localhost commented Oct 13, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.