Skip to content

Commit ad00787

Browse files
Skip prefetching anchors with the download attribute
Links marked for download often point at large binary payloads; excluding them by default avoids wasted bandwidth while keeping ignores for custom cases. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 807fdd1 commit ad00787

5 files changed

Lines changed: 51 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ quicklink.listen({
409409

410410
### Custom Ignore Patterns
411411

412+
By default, quicklink skips anchors that have a [`download`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download) attribute, since those often point at large binary payloads.
413+
412414
These filters run _after_ the `origins` matching has run. Ignores can be useful for avoiding large file downloads or for responding to DOM attributes dynamically.
413415

414416
```js

src/chunks.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ export function listen(options = {}) {
106106
});
107107

108108
timeoutFn(() => {
109-
// Find all links & Connect them to IO if allowed
110-
const links = (options.el || document).querySelectorAll('a[href]');
109+
// Find all links & Connect them to IO if allowed.
110+
// Skip anchors with the `download` attribute (large payloads).
111+
const links = (options.el || document).querySelectorAll('a[href]:not([download])');
111112
for (const link of links) {
112113
// If the anchor matches a permitted origin
113114
// ~> A `[]` or `true` means everything is allowed

src/index.mjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,15 @@ export function listen(options = {}) {
206206
});
207207

208208
timeoutFn(() => {
209-
// Find all links & Connect them to IO if allowed
209+
// Find all links & Connect them to IO if allowed.
210+
// Skip anchors with the `download` attribute — those often point at large
211+
// binary payloads that are wasteful to prefetch.
210212
const isAnchorElement = options.el && options.el.length > 0 && options.el[0].nodeName === 'A';
211-
const elementsToListen = isAnchorElement ? options.el : (options.el || document).querySelectorAll('a');
213+
const elementsToListen = isAnchorElement ? options.el : (options.el || document).querySelectorAll('a:not([download])');
212214

213215
for (const link of elementsToListen) {
216+
// NodeList of anchors may still include download links
217+
if (link.hasAttribute('download')) continue;
214218
// If the anchor matches a permitted origin
215219
// ~> A `[]` or `true` means everything is allowed
216220
if (!allowed.length || allowed.includes(link.hostname)) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Prefetch: Ignore download attribute</title>
7+
<meta name="viewport" content="width=device-width, initial-scale=1">
8+
<link rel="stylesheet" href="main.css">
9+
</head>
10+
11+
<body>
12+
<a href="1.html">Link 1</a>
13+
<a href="2.html" download>Download Link 2</a>
14+
<a href="3.html" download="report.pdf">Download Link 3</a>
15+
<a href="4.html" style="position:absolute;margin-top:900px;">Link 4</a>
16+
<script src="../../dist/quicklink.umd.js"></script>
17+
<script>
18+
quicklink.listen();
19+
</script>
20+
</body>
21+
22+
</html>

test/quicklink.spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,24 @@ mainSuite('should only prefetch links after ignore patterns allowed it', async c
207207
assert.not.ok(responseURLs.includes('https://github.githubassets.com/images/spinners/octocat-spinner-32.gif'));
208208
});
209209

210+
mainSuite('should not prefetch links with the download attribute', async context => {
211+
const responseURLs = [];
212+
context.page.on('response', resp => {
213+
responseURLs.push(resp.url());
214+
});
215+
await context.page.goto(`${server}/test-ignore-download.html`);
216+
await sleep();
217+
assert.instance(responseURLs, Array);
218+
219+
assert.ok(responseURLs.includes(`${server}/1.html`));
220+
// boolean download attribute
221+
assert.not.ok(responseURLs.includes(`${server}/2.html`));
222+
// download="filename" attribute
223+
assert.not.ok(responseURLs.includes(`${server}/3.html`));
224+
// out of viewport
225+
assert.not.ok(responseURLs.includes(`${server}/4.html`));
226+
});
227+
210228
mainSuite('should only prefetch links after ignore patterns allowed it (multiple)', async context => {
211229
const responseURLs = [];
212230
context.page.on('response', resp => {

0 commit comments

Comments
 (0)