Skip to content

Commit b964a4c

Browse files
authored
fix: improve ff handling of lazy images (#11593)
* fix: improve ff handling of lazy images * tune * tune * tune * tune * tune
1 parent b212b17 commit b964a4c

File tree

5 files changed

+41
-15
lines changed

5 files changed

+41
-15
lines changed

.changeset/plenty-zoos-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: improve handling of lazy image elements

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,30 +1888,22 @@ export const template_visitors = {
18881888
let needs_special_value_handling = node.name === 'option' || node.name === 'select';
18891889
let is_content_editable = false;
18901890
let has_content_editable_binding = false;
1891+
let img_might_be_lazy = false;
18911892

1892-
if (
1893+
if (is_custom_element) {
18931894
// cloneNode is faster, but it does not instantiate the underlying class of the
18941895
// custom element until the template is connected to the dom, which would
18951896
// cause problems when setting properties on the custom element.
18961897
// Therefore we need to use importNode instead, which doesn't have this caveat.
1897-
is_custom_element ||
1898-
// If we have an <img loading="lazy"> occurance, we need to use importNode for FF
1899-
// otherwise, the image won't be lazy. If we detect an attribute for "loading" then
1900-
// just fallback to using importNode. Also if we have a spread attribute on the img,
1901-
// then it might contain this property, so we also need to fallback there too.
1902-
(node.name === 'img' &&
1903-
node.attributes.some(
1904-
(attribute) =>
1905-
attribute.type === 'SpreadAttribute' ||
1906-
(attribute.type === 'Attribute' && attribute.name === 'loading')
1907-
))
1908-
) {
19091898
metadata.context.template_needs_import_node = true;
19101899
}
19111900

19121901
for (const attribute of node.attributes) {
19131902
if (attribute.type === 'Attribute') {
19141903
attributes.push(attribute);
1904+
if (node.name === 'img' && attribute.name === 'loading') {
1905+
img_might_be_lazy = true;
1906+
}
19151907
if (
19161908
(attribute.name === 'value' || attribute.name === 'checked') &&
19171909
!is_text_attribute(attribute)
@@ -1988,6 +1980,9 @@ export const template_visitors = {
19881980
// Then do attributes
19891981
let is_attributes_reactive = false;
19901982
if (node.metadata.has_spread) {
1983+
if (node.name === 'img') {
1984+
img_might_be_lazy = true;
1985+
}
19911986
serialize_element_spread_attributes(
19921987
attributes,
19931988
context,
@@ -2039,6 +2034,11 @@ export const template_visitors = {
20392034
}
20402035
}
20412036

2037+
// Apply the src and loading attributes for <img> elements after the element is appended to the document
2038+
if (img_might_be_lazy) {
2039+
context.state.after_update.push(b.stmt(b.call('$.handle_lazy_img', node_id)));
2040+
}
2041+
20422042
// class/style directives must be applied last since they could override class/style attributes
20432043
serialize_class_directives(class_directives, node_id, context, is_attributes_reactive);
20442044
serialize_style_directives(style_directives, node_id, context, is_attributes_reactive);

packages/svelte/src/internal/client/dom/elements/attributes.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,23 @@ function srcset_url_equal(element, srcset) {
333333
)
334334
);
335335
}
336+
337+
/**
338+
* @param {HTMLImageElement} element
339+
* @returns {void}
340+
*/
341+
export function handle_lazy_img(element) {
342+
// If we're using an image that has a lazy loading attribute, we need to apply
343+
// the loading and src after the img element has been appended to the document.
344+
// Otherwise the lazy behaviour will not work due to our cloneNode heuristic for
345+
// templates.
346+
if (!hydrating && element.loading === 'lazy') {
347+
var src = element.src;
348+
element.removeAttribute('loading');
349+
element.removeAttribute('src');
350+
requestAnimationFrame(() => {
351+
element.loading = 'lazy';
352+
element.src = src;
353+
});
354+
}
355+
}

packages/svelte/src/internal/client/dom/hydration.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function hydrate_anchor(node) {
4040
var current = /** @type {Node | null} */ (node);
4141

4242
// TODO this could have false positives, if a user comment consisted of `[`. need to tighten that up
43-
if (/** @type {Comment} */ (current)?.data !== HYDRATION_START) {
43+
if (/** @type {Comment} */ (current).data !== HYDRATION_START) {
4444
return node;
4545
}
4646

packages/svelte/src/internal/client/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export {
2626
set_attributes,
2727
set_custom_element_data,
2828
set_dynamic_element_attributes,
29-
set_xlink_attribute
29+
set_xlink_attribute,
30+
handle_lazy_img
3031
} from './dom/elements/attributes.js';
3132
export { set_class, set_svg_class, set_mathml_class, toggle_class } from './dom/elements/class.js';
3233
export { event, delegate } from './dom/elements/events.js';

0 commit comments

Comments
 (0)