Native Image Lazy-Loading: loading-attribute-eagle-polyfill

Today, Rick Viscomi noted that some sites have set eagle – instead of eager – as the value for Native Image Lazy-Loading:

While this is most likely a classic case of #damnyouautocorrect (instead of jokingly being a LOTR/Scrubs reference), that didn’t keep Jay Phelps from creating loading-attribute-eagle-polyfill to cater for those small mishaps:

A polyfill for <img loading="eagle" />. Displays an America Eagle as the placeholder of the image while the your real images are still loading.

LOL 😁 — I love the internet.

Here’s a code example on how to use, if you ever were to use it in the first place:

<head>
  <script src="https://unpkg.com/loading-attribute-eagle-polyfill/loading-attribute-eagle-polyfill.js"></script>
</head>
<body>
  <!-- Here's an example URL that artificially delays the src so you can see the proud Eagle -->
  <img
    loading="eagle"
    src="https://deelay.me/2000/https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg"
    width="300"
    height="200"
  />
</body>

loading-attribute-eagle-polyfill

ℹ️ Remember Native Image Lazy Loading being way too eager? Chrome recently updated the thresholds and are backporting the changes back to Chrome version 79:

Native Image Lazy Loading in Chrome Is Way Too Eager

UPDATE 2020.07: the thresholds have been adjusted to be less eager:

For the Web Performance Calendar Advent, Aaron Peters took a close look at Chrome’s Native Lazy Image Loading, and noticed that it’s a bit too eager:

I see the lazy-loaded images being fetched at ~ window.pageYOffset = 8500 on desktop and they become visible at ~ window.pageYOffset = 11500. So, the image lazy-load logic of Chrome seems to be: if the image is 3000 or less pixels below the viewport, load it.

3000 pixels… that is a lot!

Google’s documentation on Native Lazy Loading clearly states that the distance threshold depends on several factors:

  • The type of resource being fetched (image or iframe)
  • Whether Lite mode is enabled on Chrome for Android
  • The effective connection type

Checking the relevant Chromium Source, I see these values for the difference connection types:

//
    // Lazy image loading distance-from-viewport thresholds for different effective connection types.
    //
    {
      name: "lazyImageLoadingDistanceThresholdPxUnknown",
      initial: 5000,
      type: "int",
    },
    {
      name: "lazyImageLoadingDistanceThresholdPxOffline",
      initial: 8000,
      type: "int",
    },
    {
      name: "lazyImageLoadingDistanceThresholdPxSlow2G",
      initial: 8000,
      type: "int",
    },
    {
      name: "lazyImageLoadingDistanceThresholdPx2G",
      initial: 6000,
      type: "int",
    },
    {
      name: "lazyImageLoadingDistanceThresholdPx3G",
      initial: 4000,
      type: "int",
    },
    {
      name: "lazyImageLoadingDistanceThresholdPx4G",
      initial: 3000,
      type: "int",
    }

Yes that’s right: the slower your connection, the more eager lazy loading will work. Seems a bit of a conundrum: on one hand you don’t want images to load too soon (push for a lower distance threshold), but on the other hand you want to have the images present when scrolling (pushing for a higher distance threshold).

Native Image Lazy Loading in Chrome Is Way Too Eager →

Native image lazy-loading for the web with [loading="lazy"]

Addy Osmani, on an upcoming web feature which is about to land in Chrome 75:

The loading attribute allows a browser to defer loading offscreen images and iframes until users scroll near them. loading supports three values:

  • lazy: is a good candidate for lazy loading.
  • eager: is not a good candidate for lazy loading. Load right away.
  • auto: browser will determine whether or not to lazily load.

Not specifying the attribute at all will have the same impact as setting loading=auto.

You can also feature-detect is using this little snippet:

if ('loading' in HTMLImageElement.prototype) { 
    // Browser supports `loading`..
} else {
   // Fetch and apply a polyfill/JavaScript library for lazy-loading instead.
}

In case the browser does not support it natively, you could load up a script that uses IntersectionObserver to lazyload images.

Native image lazy-loading for the web →

👨‍🔬 Feeling experimental? You can try it out in Chrome 74 by enabling “Enable lazy frame loading” and “Enable lazy image loading” through chrome://flags

Lazy Loading images with IntersectionObserver

Smashing Magazine has an extensive article on using the aforementioned IntersectionObserver to lazy load image assets on your page.

The article first explains the difference between a regular Event and an Observer, before diving into the IntersectionObserver.

const images = document.querySelectorAll('[data-src]');
const config = {
  rootMargin: '0px 0px 50px 0px',
  threshold: 0
};
let loaded = 0;

let observer = new IntersectionObserver(function (entries, self) {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // console.log(`Image ${entry.target.src} is in the viewport!`);
      preloadImage(entry.target);
      // Stop watching and load the image
      self.unobserve(entry.target);
    }
  });
}, config);

images.forEach(image => {
  observer.observe(image);
});

Now You See Me: How To Defer, Lazy-Load And Act With IntersectionObserver