5

There is a Vue custom directive for lazy loading

 export default {
    mounted: el => {
      function loadImage() {
        console.log("loadImage called!")
        el.src = el.dataset.src
      }

      //loadImage();
  
      function handleIntersect(entries, observer) {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            loadImage();
            console.log("loadImage called! from handleIntersect")
            observer.unobserve(el);
          }
        });
      }

      function createObserver() {
        const options = {
          root: null,
          threshold: 0
        };  
        const observer = new IntersectionObserver(handleIntersect, options);
        observer.observe(el);
      }

      if (window["IntersectionObserver"]) {
        createObserver();
      } else {
        loadImage();
      }
    }
  };

Which is registered in Vue 3 component locally

<template>
    <figure>
      <img :data-src="src" @load="imageLoaded = true" v-show="imageLoaded">
     <Skeletor height="160" v-if="!imageLoaded"/>
    </figure>
    
</template>
<script>
import lazyload  from "../../../directives/lazyload"
export default {
  
  ...
  directives: {
    lazyload
  },
  data() {
    return {
      imageLoaded: false
    }
  }

}
</script>

The callback function handleIntersect is not triggered on Intersection and the images always have the data-src with the image url when I inspect them in the DevTools

However, when I uncomment calling of loadImage() function in the directive, first it is called by each elements (144 altogether) and then I see that loadImage() is called by the callback function handleIntersect as the images enter the viewport.

The example below has 3 images in the viewport

enter image description here

What is wrong with the code? Why callback function is called on Intersection once loadImages is fired for all elements and not fired at all when it is commented (console shows no output of "loadImage called!")?

Appreciate any support!

  • 1
    Hrm... Is it because that, before the image is loaded, the element has zero width/height? – Ouroborus Sep 01 '22 at 14:40
  • @Ouroborus I deleted unnecessary details, the figure has a class which applies to img tag and the img has height of 160px, width is defined by the grid, the elements belong to – Andrey Yaroshenko Sep 01 '22 at 15:50
  • @Ouroborus it turned out you were absolutely right! I didn't realize what you meant with that, but after 2 days of research it was simply as that, that img was 0 width/height. I put the v-lazyload to figure and as figure has skeleton and always visible, it works now! Thanks a lot! – Andrey Yaroshenko Sep 03 '22 at 22:44

1 Answers1

0

The reason for event not being fired was that the img was zero width/heigh or just simply put non-existent (DevTools showed style='display: none;') for Intersection Observer. When I uncommented loadImage() in the custom directive, it became visible and event was fired.

The solution was to move v-lazyload to figure and search by img tag for the target element in the custome directive, so that figure always hosts a skeletor (component from vue-skeletor package)