Skip to content

Commit

Permalink
Introduce turbo:before-prefetch event
Browse files Browse the repository at this point in the history
To allow for more fine-grained control over when Turbo should prefetch
links. This change introduces a new event that can be used to cancel
prefetch requests based on custom logic.

For example, if you want to prevent Turbo from prefetching links that
include UJS attributes, you can do so by adding an event listener for
the `turbo:before-prefetch` event and calling `preventDefault` on the
event object when the link should not be prefetched.

```javascript
document.body.addEventListener("turbo:before-prefetch", (event) => {
  if (isUJSLink(event.target)) event.preventDefault()
})

function isUJSLink(link) {
  return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-method") || link.hasAttribute("data-confirm")
}
```
  • Loading branch information
afcapel committed Feb 5, 2024
1 parent bca14e4 commit 7327a95
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 30 deletions.
15 changes: 0 additions & 15 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,3 @@ export function setConfirmMethod(confirmMethod) {
export function setFormMode(mode) {
session.setFormMode(mode)
}

/**
* Allows to define additional logic to prevent prefetching of links.
*
* By default Turbo includes a check for compatibility with older apps to prevent
* prefetching unsafe UJS links, such as those that include data-remote, data-behavior,
* data-method or data-confirm attributes, but you can override this behavior by
* providing your own check.
*
* @param checkFn Function that takes an anchor element and returns a boolean value
* to indicate whether prefetching should be prevented or not
*/
export function preventLinkPrefetch(checkFn) {
session.linkPrefetchObserver.preventLinkPrefetch = checkFn
}
12 changes: 8 additions & 4 deletions src/observers/link_prefetch_observer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
dispatch,
doesNotTargetIFrame,
getLocationForLink,
getMetaContent,
findClosestRecursively,
isUJSLink
findClosestRecursively
} from "../util"

import { StreamMessage } from "../core/streams/stream_message"
Expand All @@ -18,7 +18,6 @@ export class LinkPrefetchObserver {
constructor(delegate, eventTarget) {
this.delegate = delegate
this.eventTarget = eventTarget
this.preventLinkPrefetch = (link) => isUJSLink(link)
}

start() {
Expand Down Expand Up @@ -140,7 +139,12 @@ export class LinkPrefetchObserver {
return false
}

if (this.preventLinkPrefetch(link)) {
const event = dispatch("turbo:before-prefetch", {
target: link,
cancelable: true
})

if (event.defaultPrevented) {
return false
}

Expand Down
15 changes: 8 additions & 7 deletions src/tests/functional/link_prefetch_observer_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,20 @@ test("it doesn't prefetch the page when link has data-turbo=false", async ({ pag
await assertNotPrefetchedOnHover({ page, selector: "#anchor_with_turbo_false" })
})

test("it doesn't prefetch the page when link has UJS attributes", async ({ page }) => {
test("allows to cancel prefetch requests with custom logic", async ({ page }) => {
await goTo({ page, path: "/hover_to_prefetch.html" })
await assertNotPrefetchedOnHover({ page, selector: "#anchor_with_remote_true" })
})

test("allows to customize the check for prefetchable links", async ({ page }) => {
await goTo({ page, path: "/hover_to_prefetch.html" })
await assertPrefetchedOnHover({ page, selector: "#anchor_with_remote_true" })

await page.evaluate(() => {
window.Turbo.preventLinkPrefetch(() => false)
document.body.addEventListener("turbo:before-prefetch", (event) => {
if (event.target.hasAttribute("data-remote")) {
event.preventDefault()
}
})
})

await assertPrefetchedOnHover({ page, selector: "#anchor_with_remote_true" })
await assertNotPrefetchedOnHover({ page, selector: "#anchor_with_remote_true" })
})

test("it doesn't prefetch the page when link has the same location", async ({ page }) => {
Expand Down
4 changes: 0 additions & 4 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,6 @@ export function getLocationForLink(link) {
return expandURL(link.getAttribute("href") || "")
}

export function isUJSLink(link) {
return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-method") || link.hasAttribute("data-confirm")
}

export function debounce(fn, delay) {
let timeoutId = null

Expand Down

0 comments on commit 7327a95

Please sign in to comment.