Image inside root component is requested erroneously
Vue version
https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.global.prod.min.js
Link to minimal reproduction
https://codepen.io/schellmax01/pen/dyewKXY
Steps to reproduce
add the markup for a root component to your html document and put an image tag inside:
<div id="app">
<img style="display:none;" src="https://picsum.photos/200" width="200" height="200" loading="lazy">
</div>
then create a vue app and mount it to the dom:
const { createApp } = Vue
const app = Vue.createApp({
data() {
return {}
}
})
const rootComponent = app.mount('#app');
What is expected?
Images with the "loading" attribute set to "lazy" aren't loaded by the browser when they're invisble (style is set to "display:none").
So no request should be made by the browser for the image.
What is actually happening?
As soon as the rootComponent is created, the browser requests the image url.
Just remove the javascript line mounting the app to see that normally no image is requested.
System Info
No response
Any additional comments?
The image is requested as soon as vue adds the src attribute to the img element when creating the dom (happening in line 9579):
try {
el[key] = value; // image immediatly starts loading
}
although 'loading' attribute get attached to the img element later on, the image url gets requested immediatly after this line has run.
Interestingly, i couldn't reproduce this behaviour creating an image element in javascript and setting the src myself:
const image = document.createElement("img");
image.src="https://picsum.photos/201"; // img does not load here
image.loading="lazy";
although that seems to me to be pretty much the same to what vue does here.
A workaround is using the v-bind directive for the image src:
<img style="display:none;" :src="'https://picsum.photos/200'" width="200" height="200" loading="lazy">
Then the image does not get loaded (as expected).
related post in the vuejs forum: https://forum.vuejs.org/t/native-image-lazy-loading-possible/103907
Been watching this issue and wanted to note that we are also seeing this for other html elements such as video too.
For images - the other props being stripped are fetchpriority for proper priority hints setting. For video - the muted attribute is stripped.
The suggested workaround did not work for me, however, I found another workaround by creating a Shadow DOM element. Works fine on Chrome and Firefox.
<sr-img
src="https://picsum.photos/200"
alt=""
class="img-fluid share-rig-swiper-img"
loading="lazy"
></sr-img>
jQuery(function ($) {
function makeLazyShadowDomImageForFirefox(parent, src) {
const shadow = parent.attachShadow({ mode: 'closed' });
const img = $('<img loading="lazy" />').get(0);
img.src = src;
shadow.appendChild(img);
}
$('sr-img').each(function () {
const parent = this;
console.log(parent);
const src = parent.getAttribute('src');
makeLazyShadowDomImageForFirefox(parent, src);
});
});