fix: improve ff handling of lazy images (#11593)

* fix: improve ff handling of lazy images

* tune

* tune

* tune

* tune

* tune
pull/11580/head
Dominic Gannaway 8 months ago committed by GitHub
parent b212b17385
commit b964a4ce1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: improve handling of lazy image elements

@ -1888,30 +1888,22 @@ export const template_visitors = {
let needs_special_value_handling = node.name === 'option' || node.name === 'select'; let needs_special_value_handling = node.name === 'option' || node.name === 'select';
let is_content_editable = false; let is_content_editable = false;
let has_content_editable_binding = false; let has_content_editable_binding = false;
let img_might_be_lazy = false;
if ( if (is_custom_element) {
// cloneNode is faster, but it does not instantiate the underlying class of the // cloneNode is faster, but it does not instantiate the underlying class of the
// custom element until the template is connected to the dom, which would // custom element until the template is connected to the dom, which would
// cause problems when setting properties on the custom element. // cause problems when setting properties on the custom element.
// Therefore we need to use importNode instead, which doesn't have this caveat. // Therefore we need to use importNode instead, which doesn't have this caveat.
is_custom_element ||
// If we have an <img loading="lazy"> occurance, we need to use importNode for FF
// otherwise, the image won't be lazy. If we detect an attribute for "loading" then
// just fallback to using importNode. Also if we have a spread attribute on the img,
// then it might contain this property, so we also need to fallback there too.
(node.name === 'img' &&
node.attributes.some(
(attribute) =>
attribute.type === 'SpreadAttribute' ||
(attribute.type === 'Attribute' && attribute.name === 'loading')
))
) {
metadata.context.template_needs_import_node = true; metadata.context.template_needs_import_node = true;
} }
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
if (attribute.type === 'Attribute') { if (attribute.type === 'Attribute') {
attributes.push(attribute); attributes.push(attribute);
if (node.name === 'img' && attribute.name === 'loading') {
img_might_be_lazy = true;
}
if ( if (
(attribute.name === 'value' || attribute.name === 'checked') && (attribute.name === 'value' || attribute.name === 'checked') &&
!is_text_attribute(attribute) !is_text_attribute(attribute)
@ -1988,6 +1980,9 @@ export const template_visitors = {
// Then do attributes // Then do attributes
let is_attributes_reactive = false; let is_attributes_reactive = false;
if (node.metadata.has_spread) { if (node.metadata.has_spread) {
if (node.name === 'img') {
img_might_be_lazy = true;
}
serialize_element_spread_attributes( serialize_element_spread_attributes(
attributes, attributes,
context, context,
@ -2039,6 +2034,11 @@ export const template_visitors = {
} }
} }
// Apply the src and loading attributes for <img> elements after the element is appended to the document
if (img_might_be_lazy) {
context.state.after_update.push(b.stmt(b.call('$.handle_lazy_img', node_id)));
}
// class/style directives must be applied last since they could override class/style attributes // class/style directives must be applied last since they could override class/style attributes
serialize_class_directives(class_directives, node_id, context, is_attributes_reactive); serialize_class_directives(class_directives, node_id, context, is_attributes_reactive);
serialize_style_directives(style_directives, node_id, context, is_attributes_reactive); serialize_style_directives(style_directives, node_id, context, is_attributes_reactive);

@ -333,3 +333,23 @@ function srcset_url_equal(element, srcset) {
) )
); );
} }
/**
* @param {HTMLImageElement} element
* @returns {void}
*/
export function handle_lazy_img(element) {
// If we're using an image that has a lazy loading attribute, we need to apply
// the loading and src after the img element has been appended to the document.
// Otherwise the lazy behaviour will not work due to our cloneNode heuristic for
// templates.
if (!hydrating && element.loading === 'lazy') {
var src = element.src;
element.removeAttribute('loading');
element.removeAttribute('src');
requestAnimationFrame(() => {
element.loading = 'lazy';
element.src = src;
});
}
}

@ -40,7 +40,7 @@ export function hydrate_anchor(node) {
var current = /** @type {Node | null} */ (node); var current = /** @type {Node | null} */ (node);
// TODO this could have false positives, if a user comment consisted of `[`. need to tighten that up // TODO this could have false positives, if a user comment consisted of `[`. need to tighten that up
if (/** @type {Comment} */ (current)?.data !== HYDRATION_START) { if (/** @type {Comment} */ (current).data !== HYDRATION_START) {
return node; return node;
} }

@ -26,7 +26,8 @@ export {
set_attributes, set_attributes,
set_custom_element_data, set_custom_element_data,
set_dynamic_element_attributes, set_dynamic_element_attributes,
set_xlink_attribute set_xlink_attribute,
handle_lazy_img
} from './dom/elements/attributes.js'; } from './dom/elements/attributes.js';
export { set_class, set_svg_class, set_mathml_class, toggle_class } from './dom/elements/class.js'; export { set_class, set_svg_class, set_mathml_class, toggle_class } from './dom/elements/class.js';
export { event, delegate } from './dom/elements/events.js'; export { event, delegate } from './dom/elements/events.js';

Loading…
Cancel
Save