Native HTML: Lazyloading

Andrew Bone - Jan 11 '19 - - Dev Community

This is a slightly different post to normal, normally I talk about things that are either already implemented in a browser or have just recently been added to the spec. Today I'll be looking at something that is still waiting to be merged into the spec.

I hope this will give you a better understanding of how the spec develops over time but also give you the confidence to suggest your own improvements. The whatwg team is very open to hearing new ideas.

W3 Bug report

Our story starts in 2012. Josh, from the BBC Web team, posted this bug to the w3 website.

The BBC updated their BBC News mobile website, recently. One of the ways they optimised it for mobile devices was by deferring loading images until after the page has loaded.

I haven't looked into how this is done in detail, but they used DIVs for placeholders in the positions where the images will go. For example:

<div class="delayed-image-load" data-src="http://static.bbci.co.uk/news/200/media/images/59388000/jpg/_59388680_59388679.jpg"></div>

After the page has loaded, the DIV is changed into an IMG element.

Clearly, this isn't very semantic, and wouldn't work at all if JavaScript is disabled. But they have a good reason for doing this, which is why I believe there is a need for a way of deferring the loading of images until after the page has been parsed. This could also be applied to other embedded content elements, like IFRAME and OBJECT.

The way I think this should be done is by using the DEFER attribute, which would work in the same way as it does in the SCRIPT element.

To break this down, they'd like to be able to have an <img> tag with a defer attribute that leads to the image not rendering until the site is ready.

<img src="https://via.placeholder.com/150" defer />
Enter fullscreen mode Exit fullscreen mode

This would lead to the images not loading until after first painting making the site appear to load quicker.

GitHub Issue

The conversation went stale after a couple of years but, in 2017, Josh brought it up again this time on GitHub.

Preventing downloading images or objects until they are visible in the viewport #2806

See PR #3752

Problem

Many websites are very image heavy, but not all of those images are going to be viewed by visitors. Especially on mobile devices where most visitors do not scroll down very much; it is mostly the content at the top of the page that is consumed. Most of the images further down the page will never be viewed, but they are downloaded anyway.

This is slowing down the overall page load time, unnecessarily increasing mobile data charges for some visitors and increasing the amount of data held in memory.

Example workaround

For years, the BBC News team have been using the following method to work around this problem. Primary images at the top of the page are included in the HTML document in the typical way using an img element. However, any other images are loaded in lazily with a script. For those images, they are inidially included in the HTL document as a div which acts as a placeholder. The div is styled with CSS to have the same dimensions as the loaded image and has a grey background with a BBC logo on it.

<div class="js-delayed-image-load"
     data-src="https://ichef.bbci.co.uk/news/304/cpsprodpb/26B1/production/_96750990_totenhosen_alamy976y.jpg"
     data-width="976" data-height="549"
     data-alt="Campino of the Toten Hosen"></div>
Enter fullscreen mode Exit fullscreen mode

Eventually, a script will replace it with an img element when it is visible in the viewport.

Doing this with a script is not ideal, because:

  1. If the visitor has scripts disabled, or the script fails to load, the images won't ever appear
  2. We don't know in advance the size of the visitor's viewport, so we have to arbitrarily determine which images to load in lazily. On a news article, vistors on small viewports will only initially see the News logo and an article's hero image, but larger viewports will initially be able to see many other images (e.g. in a sidebar). But we have to favour the lowest common denominator for the sake of mobile devices. This gives users with a large viewport a strange experience where the placeholders appear for a second when they load the page.
  3. We have to wait for the script to asyncronously download and execute before any placeholders can be replaced with images.

Solution

There needs to be a native method for authors to do this without using a script.

One solution to this is to have an attribute for declaring which images or objects should not be downloaded and decoded until they are visible in the viewport. For example, <img lazyload>.*

Alternatively, a meta element could be placed in the head to globally set all images and objects to only download once they are visible in the viewport.

* An attribute with that name was proposed in the Resource Priorities spec a few years ago, but it didn't prevent the image from downloading - it just gave a hint to the browser about the ordering, which is probably not as useful in a HTTP/2 world.

Josh's idea had changed a little but the principle remained the same. Rather than a defer attribute, a lazyload attribute would be used the intention would be to only load images in the viewport.

<img src="https://via.placeholder.com/150" lazyload />
Enter fullscreen mode Exit fullscreen mode

At the time of this post, the issue has 67 thumbs up, 11 hooray and 29 heart reactions. One of GitHub's huge benefit is the ability to easily show support for issues.

This time the conversation kept going and lead to a pull request!

GitHub the pull request

The pull request comes from a Google employee named Ben. People are adding bits and looking through the spec to make sure it all makes sense. As of 21 days ago, all the comments have been addressed and they're waiting for tests to be added.

The potential spec

Let's have a look at the description, we can't pop over to Mozilla to have a look this time as we're too early.

The attribute provides a hint to the user agent to aid in deciding whether to load an element immediately or to defer loading until the element will be viewable, according to the attribute's current state.

on
Indicates a strong preference to defer fetching the element's resource until it will be viewable.

off
Indicates the element's resource must be fetched immediately, regardless of viewability.

auto
Indicates that the user agent may determine the fetching strategy (the default).

The attribute's missing value default and invalid value default are both the auto state.

Here are some practical examples

<!-- 
  this image will not be fetched until it 
  is in the viewport, meaning the page loads
  faster and uses less data.
-->
<img src="https://via.placeholder.com/150" lazyload="on" />

<!-- 
  this image will be fetched as soon as the
  page opens, this is how website work currently
-->
<img src="https://via.placeholder.com/150" lazyload="off" />

<!-- 
  this image will probably work the same as off
  but there is space for interpretation
-->
<img src="https://via.placeholder.com/150" lazyload="auto" />

<!-- 
  If the lazyload value is invalid or missing
  the attribute will default auto
-->
<img src="https://via.placeholder.com/150" lazyload="bar" />
<img src="https://via.placeholder.com/150" />
Enter fullscreen mode Exit fullscreen mode

Wrapping up

In my opinion, anything we can pass over to the browser leads to a better web. Having all images lazy load with no effort for the development team makes sense to me. Do you have any thoughts? Let me know down below.

If you have something you think the spec is missing head over to the issue and let them know. Updating the spec is a really delicate process the more help they get before any browsers have implemented it the better.

Thank you for reading, I know this was a little different to my other posts but I thought it was interesting all the same.

❤🦄🦄❤🦄❤🦄🦄❤

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .