Improve lazy/eager loading

Created on 9 November 2023, about 1 year ago
Updated 6 January 2024, 10 months ago

Problem/Motivation

The current way of preloading the images does not take into account the loading attribute. This causes images with loading=lazy to load before they enter the viewport and can slow down the loading of images with the loading=eager attribute (i.e. hero images).

Steps to reproduce

Have a page with loading=lazy images outside of the viewport, check dev tools network tab and see them load anyway.

Proposed resolution

Add some logic in resizer.js so the images only preloads if loading=eager, otherwise start preload when the image enters the viewport.
I've added a patch.

🐛 Bug report
Status

Fixed

Version

1.3

Component

Code

Created by

🇧🇪Belgium kubrick

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @kubrick
  • Status changed to Needs review 12 months ago
  • 🇳🇱Netherlands seanB Netherlands

    First of all, thanks for bringing this to my attention. We are indeed loading all lazy loaded images directly. Attached is a new version of the patch addressing some issues I had while testing it:

    • Browser lazy loading fires before our JS. This causes the default source to be loaded by the browser even though we don't always need it.
    • I move the intersectionObserver to attach to the image only once so we can do all our calculations (not just the preloading).
    • I've added an offset to the intersectionObserver so we start loading the images slightly before it is actually visible. I've added a check to updateImage() to see if an image is visible so we can keep calling this method as often as we like. It will only do things when needed.
    • I also added a check to see if an image is already being loaded so the different observer won't update the image simultaneously.
    • I have removed the preloading entirely, since it doesn't really seem to make a difference and it caused the image to be loaded twice when browser caching is disabled.

    The results are amazing. Even a relatively simple page with only a dozen of images went from 418 kB / 925 kB transferred | 403 kB / 1.1 MB resources to 185 kB / 654 kB transferred | 196 kB / 874 kB resources on first page load.

    Please review the patch before I commit it. After this one is in I will create a new release.

  • 🇧🇪Belgium kubrick

    Thanks!

    I can't seem to apply the patch. Is this for 1.3.2?

    Maybe we don't even need the intersection observer if you got rid of the preloading.
    Wouldn't the browser's native support for the lazy attribute take care of everything?
    Or do they load immediately if you override the src on the image?

  • 🇳🇱Netherlands seanB Netherlands

    The patch should apply to the latest dev version (you can load that via composer using dev-1.3.x#ced7e669a0e672d5ceb83dc487c13b00952fd7fc).

    We definitely need an intersection observer, since images are immediately loaded when you change the src. That is pretty much the bug we are solving here.

  • 🇧🇪Belgium kubrick

    During testing I had some images not load because the src did not get set.

    I've made some changes to the observers:

    • Only add the resize observer once the lazy image hits the intersection observer.
    • Remove the check in updateImage() that checks if the image is in the viewport, since this function can't run anymore before the IntersectionObserver fires.
    • Remove the intersection observer after the first intersection. (not related to the bug but might be better to remove it once it's not needed anymore)

    Here's some videos with the network tab open:

    Some images not loading: https://www.dropbox.com/scl/fi/6jrb522ue812ofawvcjbt/1.mov?rlkey=puc0r3v...
    New patch: https://www.dropbox.com/scl/fi/xjzx3sq7whqdstglwebvu/2.mov?rlkey=jx1uz80...

    The smaller images wouldn't really need easy_responsive_images but works as a demo.

  • 🇳🇱Netherlands seanB Netherlands

    Coul you create an interdiff for the patch?

  • 🇧🇪Belgium kubrick

    Sure!

    I also rerolled the patch since my Prettier changed the formatting on the previous patch.

    New at contributing so bear with me 🙈

    • seanB authored 94469349 on 1.3.x
      Issue #3400525 by kubrick, seanB: Improve lazy/eager loading
      
  • Status changed to Fixed 11 months ago
  • 🇳🇱Netherlands seanB Netherlands
    1. +++ b/js/resizer.js
      @@ -172,7 +160,10 @@
      +            intersectionObserver.unobserve(entry.target);
      

      This makes sense, it optimizes the executed JavaScript. Nice!

    2. +++ b/js/resizer.js
      @@ -172,7 +160,10 @@
      +            resizeObserver.observe(entry.target.parentNode);
      

      This is also pretty clever, it allows us to remove the check in updateImage and also optimizes the JavaScript. thanks!

    3. +++ b/js/resizer.js
      @@ -187,14 +178,16 @@
      +          intersectionObserver.observe(image);
      

      This seems to be missing the part where we remove the "src" for lazy loaded images. I added that back, since the browser seems to load the original sources before we update the src to the prefered image style. Since the original sources are pretty much immediately replaced, this seems like a nice extra optimization. It needs some extra manual testing though.

    I've committed the latest patch with some minor changes. Please help me test the latest commit (dev-1.3.x#94469349953435ec2a948dcffd4507fd0e12f267). If you can confirm everything works I will roll a new release.

  • 🇧🇪Belgium kubrick

    Thanks! Looking good!

    Regarding 3.

    I noticed the same issue again with certain smaller images not loading because the src attribute was missing.
    This happens if the original image is wider than the width of its parent, so the src does not get re-added in updateImage().

    I've added a patch that always runs the updateImage logic if the src attribute is missing.

    Besides that is also noticed original images sometimes loading even with the removeAttribute('src') line. When they're inside the viewport when the page loads the images are loaded before the JavaScript can remove the src. I don't think removing the src in the Twig templates would be a good fix so probably best to leave it.

    • 1448b3f7 committed on 1.3.x
      Issue #3400525 by kubrick, seanB: Improve lazy/eager loading
      
  • 🇳🇱Netherlands seanB Netherlands

    Thanks! Nice find! Committed. I'll create a new release now as well.

  • Status changed to Fixed 10 months ago
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024