diff --git a/src/core/drive/prefetch_cache.js b/src/core/drive/prefetch_cache.js
index ecca297b7..79b7331d1 100644
--- a/src/core/drive/prefetch_cache.js
+++ b/src/core/drive/prefetch_cache.js
@@ -10,14 +10,16 @@ class PrefetchCache {
}
}
- setLater(url, request, ttl) {
+ setLater(url, request, ttl, delay) {
this.clear()
- this.#prefetchTimeout = setTimeout(() => {
- request.perform()
- this.set(url, request, ttl)
- this.#prefetchTimeout = null
- }, PREFETCH_DELAY)
+ if (delay === "0") {
+ this.#setNow(url, request, ttl)
+ } else {
+ delay = Number(delay) || PREFETCH_DELAY
+
+ this.#enqueue(url, request, ttl, delay)
+ }
}
set(url, request, ttl) {
@@ -28,6 +30,18 @@ class PrefetchCache {
if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout)
this.#prefetched = null
}
+
+ #enqueue(url, request, ttl, delay) {
+ this.#prefetchTimeout = setTimeout(() => {
+ this.#setNow(url, request, ttl)
+ this.#prefetchTimeout = null
+ }, delay)
+ }
+
+ #setNow(url, request, ttl) {
+ request.perform()
+ this.set(url, request, ttl)
+ }
}
export const cacheTtl = 10 * 1000
diff --git a/src/observers/link_prefetch_observer.js b/src/observers/link_prefetch_observer.js
index 6265f075a..f3f100fa8 100644
--- a/src/observers/link_prefetch_observer.js
+++ b/src/observers/link_prefetch_observer.js
@@ -78,7 +78,9 @@ export class LinkPrefetchObserver {
target
)
- prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl)
+ const delay = link.dataset.turboPrefetchDelay || getMetaContent("turbo-prefetch-delay")
+
+ prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl, delay)
}
}
}
diff --git a/src/tests/fixtures/hover_to_prefetch.html b/src/tests/fixtures/hover_to_prefetch.html
index d47be49db..93d34ec62 100644
--- a/src/tests/fixtures/hover_to_prefetch.html
+++ b/src/tests/fixtures/hover_to_prefetch.html
@@ -14,6 +14,9 @@
Hover to prefetch me
Hover to prefetch me
+ Hover to prefetch me
+ Hover to prefetch me
+ Hover to prefetch me
diff --git a/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html
new file mode 100644
index 000000000..2efc23882
--- /dev/null
+++ b/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Hover to Prefetch
+
+
+
+
+
+
+ Hover to prefetch me
+
+
diff --git a/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html
new file mode 100644
index 000000000..3a6866769
--- /dev/null
+++ b/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Hover to Prefetch
+
+
+
+
+
+
+ Hover to prefetch me
+
+
diff --git a/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html
new file mode 100644
index 000000000..0803b5a65
--- /dev/null
+++ b/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Hover to Prefetch
+
+
+
+
+
+
+ Hover to prefetch me
+
+
diff --git a/src/tests/functional/link_prefetch_observer_tests.js b/src/tests/functional/link_prefetch_observer_tests.js
index 735efb7fb..4d814fe5f 100644
--- a/src/tests/functional/link_prefetch_observer_tests.js
+++ b/src/tests/functional/link_prefetch_observer_tests.js
@@ -208,6 +208,93 @@ test("it prefetches links with a delay", async ({ page }) => {
assertRequestMade(requestMade)
})
+test("it allows to customize the delay with a data-turbo-prefetch-delay attribute", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_with_custom_delay")
+ await sleep(200)
+
+ assertRequestNotMade(requestMade)
+
+ await sleep(150)
+
+ assertRequestMade(requestMade)
+})
+
+test("it allows to customize the delay with a turbo-prefetch-delay a meta tag", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch_with_delay_on_meta_tag.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_for_prefetch")
+
+ await sleep(200)
+
+ assertRequestNotMade(requestMade)
+
+ await sleep(150)
+
+ assertRequestMade(requestMade)
+})
+
+test("it prefetches immediately with a data-turbo-prefetch-delay attribute set to 0", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_with_zero_delay")
+
+ assertRequestMade(requestMade)
+})
+
+test("it prefetches immediately with a turbo-prefetch-delay meta tag set to 0", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch_with_zero_delay_on_meta_tag.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_for_prefetch")
+
+ assertRequestMade(requestMade)
+})
+
+test("it uses the default delay with a data-turbo-prefetch-delay attribute set to an invalid value", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_with_invalid_delay")
+ await sleep(75)
+
+ assertRequestNotMade(requestMade)
+
+ await sleep(100)
+
+ assertRequestMade(requestMade)
+})
+
+test("it uses the default delay with a turbo-prefetch-delay meta tag set to an invalid value", async ({ page }) => {
+ await goTo({ page, path: "/hover_to_prefetch_with_invalid_delay_on_meta_tag.html" })
+
+ let requestMade = false
+ page.on("request", async (request) => (requestMade = true))
+
+ await page.hover("#anchor_for_prefetch")
+ await sleep(75)
+
+ assertRequestNotMade(requestMade)
+
+ await sleep(100)
+
+ assertRequestMade(requestMade)
+})
+
test("it cancels the prefetch request if the link is no longer hovered", async ({ page }) => {
await goTo({ page, path: "/hover_to_prefetch.html" })