Skip to content

Commit 50a7ea0

Browse files
authored
fix(listings): lazy loading for listing auto-detected images (#13953)
* fix: `listing:image-lazy-loading` does not work for values of `true` or `false` * test: update tests for lazy loading in more scenarios * chore: update changelog entry Fixes #13951
1 parent a4e7370 commit 50a7ea0

16 files changed

Lines changed: 168 additions & 44 deletions

File tree

news/changelog-1.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ All changes included in 1.9:
9595
- ([#13716](https://github.com/quarto-dev/quarto-cli/issues/13716)): Fix draft pages showing blank during preview when pre-render scripts are configured.
9696
- ([#13847](https://github.com/quarto-dev/quarto-cli/pull/13847)): Open graph title with markdown is now processed correctly. (author: @mcanouil)
9797
- ([#13910](https://github.com/quarto-dev/quarto-cli/issues/13910)): Add support for `logo: false` to disable sidebar and navbar logos when using `_brand.yml`. Works in website projects (`sidebar.logo: false`, `navbar.logo: false`) and book projects (`book.sidebar.logo: false`, `book.navbar.logo: false`).
98+
- ([#13951](https://github.com/quarto-dev/quarto-cli/issues/13951)): Fix `image-lazy-loading` not applying `loading="lazy"` attribute to auto-detected listing images.
9899

99100
### `book`
100101

src/project/types/website/listing/website-listing-read.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,16 @@ export function completeListingItems(
393393
debug(`[listing] Processing image match`);
394394
const progressive = imgMatch[1] === "true";
395395
const imgHeight = imgMatch[2];
396-
const listingId = imgMatch[3];
397-
const docRelativePath = imgMatch[4];
396+
const lazy = imgMatch[3] !== "false";
397+
const listingId = imgMatch[4];
398+
const docRelativePath = imgMatch[5];
398399
const docAbsPath = join(projectOutputDir(context), docRelativePath);
399400
const imgPlaceholder = imagePlaceholder(
400401
listingId,
401402
docRelativePath,
402403
progressive,
403404
imgHeight,
405+
lazy,
404406
);
405407
debug(`[listing] ${docAbsPath}`);
406408
debug(`[listing] ${imgPlaceholder}`);
@@ -435,6 +437,7 @@ export function completeListingItems(
435437
},
436438
progressive,
437439
imgHeight,
440+
lazy,
438441
);
439442

440443
debug(`[listing] replacing: ${docAbsPath}`);
@@ -454,7 +457,7 @@ export function completeListingItems(
454457
};
455458
fileContents = fileContents.replace(
456459
imgPlaceholder,
457-
imageSrc(imagePreview, progressive, imgHeight),
460+
imageSrc(imagePreview, progressive, imgHeight, lazy),
458461
);
459462
} else {
460463
debug(`[listing] using empty div: ${docAbsPath}`);
@@ -501,11 +504,14 @@ export function imagePlaceholder(
501504
file: string,
502505
progressive: boolean,
503506
height?: string,
507+
lazy?: boolean,
504508
): string {
505509
return file
506510
? `<!-- img(9CEB782EFEE6)[progressive=${
507511
progressive ? "true" : "false"
508-
}, height=${height ? height : ""}]:${id}:${file} -->`
512+
}, height=${height ? height : ""}, lazy=${
513+
lazy === false ? "false" : "true"
514+
}]:${id}:${file} -->`
509515
: "";
510516
}
511517

@@ -517,7 +523,7 @@ const descriptionPlaceholderRegex =
517523
/<!-- desc\(5A0113B34292\)\[max\=(.*)\]:(.*) -->/;
518524

519525
const imagePlaceholderRegex =
520-
/<!-- img\(9CEB782EFEE6\)\[progressive\=(.*), height\=(.*)\]:(.*):(.*) -->/;
526+
/<!-- img\(9CEB782EFEE6\)\[progressive\=(.*), height\=(.*), lazy\=(.*)\]:(.*):(.*) -->/;
521527

522528
function hydrateListing(
523529
format: Format,
@@ -1202,8 +1208,14 @@ async function listItemFromFile(
12021208
}
12031209
}
12041210

1205-
function imageSrc(image: PreviewImage, progressive: boolean, height?: string) {
1206-
return `<p class="card-img-top"><img ${
1211+
function imageSrc(
1212+
image: PreviewImage,
1213+
progressive: boolean,
1214+
height?: string,
1215+
lazy?: boolean,
1216+
) {
1217+
const lazyAttr = lazy === false ? "" : "loading='lazy' ";
1218+
return `<p class="card-img-top"><img ${lazyAttr}${
12071219
progressive ? "data-src" : "src"
12081220
}="${image.src}" ${image.alt ? `alt="${image.alt}" ` : ""}${
12091221
height ? `style="height: ${height};" ` : ""

src/project/types/website/listing/website-listing-template.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,13 +447,15 @@ export function reshapeListing(
447447
listingId: string,
448448
itemNumber: number,
449449
itemPath: string,
450+
lazy?: boolean,
450451
) => {
451452
const pageSize = listing[kPageSize];
452453
return imagePlaceholder(
453454
listingId,
454455
itemPath,
455456
itemNumber > pageSize,
456457
listing[kImageHeight] as string,
458+
lazy,
457459
);
458460
};
459461

src/resources/projects/website/listing/item-default.ejs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ print(`<div class="metadata-value listing-${field}">${listing.utilities.outputLi
4545
<% if (item.image) { %>
4646
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']) %>
4747
<% } else { %>
48-
<%= listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref) %>
48+
<%= listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref, item['image-lazy-loading'] ?? listing['image-lazy-loading']) %>
4949
<% } %>
5050
</a></div>
5151
```

src/resources/projects/website/listing/item-grid.ejs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ return !["title", "image", "image-alt", "date", "author", "subtitle", "descripti
5757
<% } else { %>
5858

5959
```{=html}
60-
<%= listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref) %>
60+
<%= listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref, item['image-lazy-loading'] ?? listing['image-lazy-loading']) %>
6161
```
6262

6363
<% } %>

src/resources/projects/website/listing/listing-table.ejs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ if (field === "image") {
5757
if (item.image) {
5858
value = listing.utilities.img(itemNumber, item[field], "", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']);
5959
} else {
60-
value = listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref);
60+
value = listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref, item['image-lazy-loading'] ?? listing['image-lazy-loading']);
6161
}
6262
}
6363
return listing.utilities.outputLink(item, field, value, `listing-${field}`);

tests/docs/smoke-all/listings/image-lazy-loading/blog/index-default-eager.qmd

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,22 @@ listing:
77
- image
88
image-lazy-loading: false
99
_quarto:
10-
html:
11-
ensureHtmlElements:
12-
-
13-
- "img[src='https://placeholder.co/100/100.png']"
14-
- "img[src='https://placeholder.co/200/200.png']"
15-
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
16-
-
17-
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
18-
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
10+
render-project: true
11+
tests:
12+
html:
13+
ensureHtmlElements:
14+
-
15+
- "img[src='https://placeholder.co/100/100.png']"
16+
- "img[src='https://placeholder.co/200/200.png']"
17+
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
18+
- "img[src='https://placeholder.co/400/400.png']"
19+
- "img[src='https://placeholder.co/500/500.png']"
20+
- "img[loading='lazy'][src='https://placeholder.co/600/600.png']"
21+
-
22+
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
23+
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
24+
- "img[loading='lazy'][src='https://placeholder.co/400/400.png']"
25+
- "img[loading='lazy'][src='https://placeholder.co/500/500.png']"
1926
---
2027

2128
## Hello, here's some listings with `image-lazy-loading: false`

tests/docs/smoke-all/listings/image-lazy-loading/blog/index-default.qmd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ listing:
66
- title
77
- image
88
_quarto:
9+
render-project: true
910
tests:
1011
html:
1112
ensureHtmlElements:
12-
-
13+
-
1314
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
1415
- "img[src='https://placeholder.co/200/200.png']"
1516
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
17+
- "img[loading='lazy'][src='https://placeholder.co/400/400.png']"
18+
- "img[src='https://placeholder.co/500/500.png']"
19+
- "img[loading='lazy'][src='https://placeholder.co/600/600.png']"
1620
-
1721
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
22+
- "img[loading='lazy'][src='https://placeholder.co/500/500.png']"
1823
---
1924

2025
## Hello, here's some listings

tests/docs/smoke-all/listings/image-lazy-loading/blog/index-grid-eager.qmd

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,22 @@ listing:
77
- image
88
image-lazy-loading: false
99
_quarto:
10-
html:
11-
ensureHtmlElements:
12-
-
13-
- "img[src='https://placeholder.co/100/100.png']"
14-
- "img[src='https://placeholder.co/200/200.png']"
15-
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
16-
-
17-
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
18-
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
10+
render-project: true
11+
tests:
12+
html:
13+
ensureHtmlElements:
14+
-
15+
- "img[src='https://placeholder.co/100/100.png']"
16+
- "img[src='https://placeholder.co/200/200.png']"
17+
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
18+
- "img[src='https://placeholder.co/400/400.png']"
19+
- "img[src='https://placeholder.co/500/500.png']"
20+
- "img[loading='lazy'][src='https://placeholder.co/600/600.png']"
21+
-
22+
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
23+
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
24+
- "img[loading='lazy'][src='https://placeholder.co/400/400.png']"
25+
- "img[loading='lazy'][src='https://placeholder.co/500/500.png']"
1926
---
2027

2128
## Hello, here's some listings with `image-lazy-loading: false`

tests/docs/smoke-all/listings/image-lazy-loading/blog/index-grid.qmd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ listing:
66
- title
77
- image
88
_quarto:
9+
render-project: true
910
tests:
1011
html:
1112
ensureHtmlElements:
12-
-
13+
-
1314
- "img[loading='lazy'][src='https://placeholder.co/100/100.png']"
1415
- "img[src='https://placeholder.co/200/200.png']"
1516
- "img[loading='lazy'][src='https://placeholder.co/300/300.png']"
17+
- "img[loading='lazy'][src='https://placeholder.co/400/400.png']"
18+
- "img[src='https://placeholder.co/500/500.png']"
19+
- "img[loading='lazy'][src='https://placeholder.co/600/600.png']"
1620
-
1721
- "img[loading='lazy'][src='https://placeholder.co/200/200.png']"
22+
- "img[loading='lazy'][src='https://placeholder.co/500/500.png']"
1823
---
1924

2025
## Hello, here's some listings

0 commit comments

Comments
 (0)