Subscribe on changes!

Image inside root component is requested erroneously

avatar
Oct 12th 2022

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).

avatar
Dec 1st 2022

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.

avatar
Mar 7th 2023

Is there any update on this? It's impacting the SEO of my pages that use Vue.

avatar
Mar 9th 2023

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);
  });
});
avatar
Jul 12th 2023

Checking if there are any updates for this, vue shouldn't really be breaking native browser functionality.