A standard way to lazy load images


#1

Lazy loading images is currently achieved via JavaScript libraries; see this post for a recent overview of such libraries. Wikimedia is even experimenting with using a service worker for this.

Are there any proposals to enable image lazy loading declaratively in HTML (or even HTTP)?


Linking to local (image) resources
Manual priority control of resource fetching
[Proposal] Manually loading image sources based on preset conditions
#2

The Web Perf group went through several attempts in doing so. The latest one is https://w3c.github.io/resource-hints/

Past attempts include http://www.w3.org/TR/resource-priorities/


#3

This is a subject I’m very passionate about, because I feel that if there’s no need to use javascript libraries (which are unable to completely do this task because there’s no way to subscribe to a ‘print’ event and download the images then, and don’t work without javascript without a cumbersome <noscript> section), we can have a really fast web because more people will lazy load their images.

This is especially important in mobile browsers, where downloading images comes at a performance (uses up paralell connections to a server, delaying other important resources) and actual monetary cost.

I’ve drafted this proposal a few days ago:


#4

Ian Hickson also tackled this a year+ ago with a proposal that seems to have gone nowhere:

https://lists.w3.org/Archives/Public/public-whatwg-archive/2014Aug/0177.html


#5

Semi-related, here’s a proposal that would make doing this sort of thing much easier in Javascript:

https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/eLxh8xUp3j4/m7RH4_nLBgAJ

The arguments for a declarative (or at least non-Javascript) solution are strong, though:

  1. It’s an evidently important use case that keeps coming up
  2. Javascript solutions require keeping content images out of the HTML on first load, which is bad.

Wikipedia’s Service Worker experiments are super interesting!

Here’s a crazy idea: what if this were solved at the browser level, and users could opt-in (or maybe, eventually, out) of lazyloading-by-default?


#6

This is implemented in Internet Explorer 11 (and maybe Edge) as

<img src="whatever.jpg" lazyload>

Which I believe was taken from the aforementioned (and dead, sadly) Resource Priorities spec. Sadly, the replacement, Resource Hints, has scaled back and is only about giving URLs to the lookahead preparser ahead of time.

Should the exact behavior be specifiable? Do you want to load images when the network is otherwise idle, or only once the image approaches the viewport? Something like lazyload="visible" or lazyload="idle" could work. Such behavior might require width, height, and sizes attributes so the browser doesn’t have to reflow after fetching the image.

We have a few attributes already designed for loading and execution behavior. Maybe they should get expanded to all request-making elements, like:

<img async>
<link rel="stylesheet" defer>
<script lazyload>

#7

async and defer determine when a script is executed, not loaded, so I don’t know if they make much sense applied to <img> (which, if you equate execution with display, are already kinda async?).

There are two, related goals here:

  1. Load critical bytes first (aka, bring the SpeedIndex down)
  2. Don’t load unnecessary bytes at all (aka bring the bytes-over-the-wire down)

Anything that did #2 well enough would also achieve #1, but it seems hard to do perfectly. Trying to load images Just-In-Time™ is going to mean some images get loaded noticeably late or not at all in at least some edge cases (e.g. initial load on a train, scroll in a tunnel).

All of the JavaScript libraries in the link up top, and every library I’ve seen, go for #2, so it feels more important?


#8

Loading them just-in-time isn’t as common, but it’s one of the reasons why AMP HTML makes you use <amp-img> with mandatory sizing attributes:

The runtime may choose to delay or prioritize resource loading based on the viewport position, system resources, connection bandwidth, or other factors.

I think the idea is, if the user decides they aren’t interested in an article while reading it, they’re not penalized for loading images they’d never see.

Still, it’s worthwhile just trying to get Goal #1 by itself, so if this complicates matters unnecessarily, just leave it for later.


#9

With respimg hat on

Pros for a lazy attribute:

  • Developer convenience would mean this pattern is more often used

Cons:

  • Fear of abuse that would cause overall perf regressions.

All in all:

  • A lazy attribute has no (significant) perf advantage over script-based solutions
  • The browser would have to wait until layout is complete in order to know if a certain resource should be fetched or not.
  • A ServiceWorker based solution can resolve the problem for the “JS is not running” case, even if with some overhead
  • IntersectionObserver would make it easier to implement a JS loading scheme without scroll-related hacks

The main problem with both a JS based solution and a lazy attribute, is that both would require you to add to markup which images should be lazy loaded, which is something that can change based on the viewport :disappointed:


#10

Also, in terms of

there’s a good chance that having a commonly used lazy-loading solution (native or JS based) would result in regressions on that metric if overly used, since in-the-viewport images are likely to be marked as lazyload and delayed.

On that front, the browser should prioritize in-the-viewport images over out-of-viewport ones, and download everything needed for visual completeness first. I’m not sure lazy loading would help here.


#11

Sometimes (eg, in a carousel) the author definitely knows that certain images won’t be visible on first load, no matter what the viewport looks like. A non-hacky way to explicitly request deferred loading for just those images (without marking up imgs-without-srces or whatever) would be useful.

But my hunch is that it’s much more common for some authors to want to slap data-lazys onto large swaths of maybe-visible or probably-not-visible images by default, which would sometimes result in the SpeedIndex regressions which you describe (with the tradeoff being that people who don’t see all of the images on the page don’t load them).

I should compile some links of popular sites that lazily load to better understand how it’s being used in the wild, now, and why… but I’m about to leave for a weeklong vacation.

On that front, the browser should prioritize in-the-viewport images over out-of-viewport ones, and download everything needed for visual completeness first. I’m not sure lazy loading would help here.

This sounds ideal, but would be impossible without waiting for layout, which would probably cause regressions?


#12

To start, let’s clarify what “lazy load” means… I see different folks using different definitions on this thread.

  1. As a prioritization signal that affects the order in which images are loaded.
  2. As an ‘on-demand loading’ signal that allows the user agent to skip loading and/or load image when some criteria are met (e.g. visibility).

For (1), I’d argue that “lazyload” is the wrong concept; this is prioritization, plain and simple. We need an API that will allow developers to customize the network-layer priority of any given resource fetch:

  • override default UA prioritization logic - e.g. not all images are same priority; some images are just as important as blocking CSS; some CSS is less important than images; etc.
  • this API is not restricted to images, it applies to all fetches initiated by the browser.
  • this API allows developers to downgrade and upgrade priority of fetches.

(Side note: IE’s implementation of lazyload is prioritization… All it does is downgrade the priority of the image in their net-stack.)

For (2), I remain very skeptical that this is something that browsers should take on. There is a lot of magic assumptions here that don’t stand up to scrutiny of being a platform feature…

  • Deferred resource loading is not an unconditional win for the user. Waking up the mobile radio is very costly and will consume more energy; it can cause delayed rendering; it can cause more reflows, etc. Perhaps you’re willing to sacrifice some of these if the user is sensitive to amount of downloaded data, but that’s just one consideration of many - e.g. type of network, type of resources being fetched, etc, can change the balance of the equation.
  • Deferred resource loading “done well” is highly application specific. For example, if you’re building an infinite scroller you may want to tweak how far you prefetch based on users scroll position - e.g. preload next item vs three screens ahead vs … whatever logic makes sense for your app.
  • Deferred resource loading use cases are not restricted to images. As Yoav pointed out earlier, the logic may change based on current layout and active breakpoint – element attributes can’t account for this, at least not easily.

Long story short, I think this space is too complex to reduce it to one or several keywords that you sprinkle on your elements. The platforms’ job here is to give developers the tools to build own “deferred loading” logic. Some of these building blocks are:

  • A way to efficiently query element visibility information - see intersection observer.
  • A way to prioritize fetch requests via ~fetch prioritization API - addresses (1), helps (2).
  • A way to preload / prefetch resources - see resource hints, preload.
  • A way to determine if user is sensitive to data use - see save-data.
  • A way to determine connectivity information - see NetInfo.
  • A way to schedule processing/decoding/whatever without causing jank - see requesIdleCallback.

The above is not a complete list, but those are some of the most critical building blocks that enable developers to build own ‘deferred loading’ implementations that make sense for their particular use case.


#13

One frustration that comes up a lot is that we can’t have things like

<object type="image/svg+xml">
  <img>
</object>

without penalizing users with a double-download. The ability to “turn off the preparser” might not be an ability browsers want to give to developers, but it’s definitely frustrating that the built-in, scriptless, semantic fallback abilities of HTML are rather useless when preparsers clobber any performance benefits from using them.

I personally have talked with a few people who want lazyload for a reason like that, so maybe that’s yet another tangentially-related thing that is getting lumped in.


#14

That sounds like a bug. Can you file bugs on crbug.com and wkbug.com and send them my way? (You should probably also file ones on other browsers :D)


#15

That is already what Blink/Chrome does. Images start their lives at lowest priority, and get upgraded as they become visible. That means that if there aren’t more important resource, images will get requested according to their order, but if there are, they are requested only once their priority gets bumped.


#16

It’s per-spec that img still loads even inside object, but we could change the spec (assuming it’s Web-compatible enough). Similarly for img in video and audio probably.


#17

@zcorpan - didn’t know that. Let’s fix it. (and in any case, no need to preload these images)


#18

@yoavweiss, @zcorpan – previously, re: <img> in <object>/<audio>/<video>:


#19

A little browsing through a the results of a hacky HTTPArchive query shows that the most popular pages using anything called “lazyload” are all doing deferred loading, either because they are long pages stuffed with images (lots of catalog-style shopping sites and news sites) or they have carousels.

Here’s my argument for ‘deferred’ lazyloading in a nutshell: images that definitely or very-probably won’t be visible on pageload shouldn’t be loaded until they’re needed. There should be a simple way for authors to achieve this without invalid markup or transparent gifs. Tens of thousands of sites are currently using this library or something like it to serve up <img>s without srcs. That’s a problem!


#20

@yoavweiss

That is already what Blink/Chrome does.

So let me see if I understand: the speculative pre-parser combs through a page and compiles a list of all of the external resource URLs. It sticks images at the bottom of this the list and kicks off loading as many of these resources as it can. If the queue still exists after layout is complete, images that are in the viewport are bumped up.