How to manipulate the DOM of a page before it finishes loading?

Asked

Viewed 274 times

7

I am making a Lazy Loader in pure javascript, currently I put my embed script at the end of the HTML that identifies the images and, if they are not ready (for example loaded from the cache), this changes the image to a lighter placeholder and when the user arrives at the image is loaded the original image.

I wonder if there is a way for me to find img tags while the DOM is loaded, to be able to replace their src by my placeholder even before the request is started by the original image (currently non-ccached requests appear in the inspector as canceled).

I currently use the following at the bottom of the page to find the images.

<script>
    const srcs = Array();
    const srcSets = Array();
    const images = document.querySelectorAll("img");
    let isRequired = false;
    let index = 0;
    for (let i = 0; i < images.length; i++) {
      if (!images[i].complete) {
        isRequired = true;
        srcs.push(images[i].src);
        srcSets.push(images[i].srcset);
        images[i].src = "placeholder.svg"; /// Usar o placeholder de sua escolha, de preferência um SVG pré conectado
        images[i].srcset = "";
        images[i].setAttribute("index", index);
        index++;
      }
    }
    
    if (isRequired) {
      window.onload = () => {
        const script = document.createElement("script");
        script.src = "loader.js";
        document.querySelector("body").appendChild(script);
      };
    }
</script>

And the "Loader.js" which is responsible for loading the images as they appear on the screen using the native Intersection Observer API.

const imageLoader = (entries) => {
  entries.map((e) => {
    if (e.isIntersecting) {
      const i = e.target.getAttribute("index");
      e.target.src = srcs[i];
      e.target.srcset = srcSets[i];
      observer.unobserve(e.target);
    }
  });
};

const observer = new IntersectionObserver(imageLoader, {
  threshold: 0.1,
});

const observe = () => {
  const images = document.querySelectorAll("img");
  for (let i = 0; i < images.length; i++) {
    if (images[i].getAttribute("index") !== null) {
      observer.observe(images[i]);
    }
  }
};

observe();

3 answers

2


Try to manipulate the src of tags <img> before these are loaded into the user’s browser is like trying to manipulate something that does not exist.

I believe that the best strategy for your case would be to do the reverse of what you described, ie, send all tags <img> with your placeholder (as light as possible), and then try to asynchronously load the heaviest images.

  • I know putting a script right after a tag <img>, I can interact with her. The question is whether you know that these images were found while the DOM is built and already replace them, such as creating an event that is shot whenever an image is found, Anyway, thanks for the suggestion!

0

First change the property of all your src images to data-src and add a new src with your placeholder.

Change the page load script to the following

const images = document.querySelectorAll("img");

const imagesNotCompleted = images.filter(x => !x.complete);

const isRequired = imagesNotCompleted.length !== 0;

if (isRequired) {
  window.onload = () => {
    const script = document.createElement("script");
    script.src = "loader.js";
    document.querySelector("body").appendChild(script);
  };
}

In the implementation of the Loader

const imageLoader = (entries) => {

  for(let el of entries.filter(x => x.isIntersecting)){
    el.target.src = el.target.dataset.src;
    observer.unobserve(e.target);
  }
  
};

const observer = new IntersectionObserver(imageLoader, {
  threshold: 0.1,
});

const observe = () => {
  const images = document.querySelectorAll("img");
  
  images.filter(x => x.dataset.src)
    .forEach(x => observer.observe(x));
};

observe();

See if this works

-2

This example can help you, the getInView function takes the elements that are inside the scroll at the moment, can do this to load the images dynamically and then set a right property in html as 'isLoaded=true' ai in the next Scrolls you can check only the images that are still isLoaded false, preventing the images from being loaded in each user scroll.

function getInView(event) {
  const scrolling = event.target.scrollingElement;

  const Iniview = scrolling.scrollTop;
  const finalView = scrolling.scrollTop + scrolling.clientHeight;

  const spans = Array.from(document.querySelectorAll('span'));
  const spansInViewNow = spans
    //.filter(x => !x.isLoaded)
    .filter(x => x.offsetTop + x.offsetHeight >= Iniview &&
      x.offsetTop + x.offsetHeight <= finalView)
    .map(x => x.innerText);
    //.map(x => { x.isLoaded = true; return x.innerText; });

  console.log(spansInViewNow);
  return spansInViewNow;
}

const bodyElement = document.querySelector('body');
bodyElement.onscroll = getInView;
.container {
  min-height: 500px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
<html>

<body class='container'>
  <span>1</span>

  <span>2</span>

  <span>3</span>
</body>

</html>

  • I’m already using the Intersection Observer API to do this, the problem is finding a way to replace the src of the images as DOM identifies them in the page load.

  • @Wagnerkramer why not directly place all images in html with src = placeholder.svg and use a data-src attribute as a way to get the original src of the image when the browser is loaded and change the src with the Observer lib?

  • I’ll make an answer suggesting that

Browser other questions tagged

You are not signed in. Login or sign up in order to post.