-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preventing downloading images or objects until they are visible in the viewport #2806
Comments
Hmm, this was previously discussed at https://www.w3.org/Bugs/Public/show_bug.cgi?id=17842, but GitHub is more friendly for people. Let me merge that thread into here, but please please please read all the contents of the discussion there, as this is very well-trod ground and we don't want to have to reiterate the same discussions over again. |
I've just spent an hour reading the thread on the original bug report (which @JoshTumath actually reported). There was initially confusion between two features: (1) Being able to tag images as "not important" so that the browser can give priority to other resources. (2) Being able to opt in to loading specific images only at the point where they are in the viewport or just about to enter it. This issue is specifically for (2). I will refer to this as "lazy loading". The thread goes around in circles and doesn't really have a clear outcome, although the implementations discussed still seem valid and relevant today (Jake's summary in comment 49 is a good point to start at if you don't want to read the entire thread). I'm going to try not to repeat too much from that thread, but it has been 5 years now and as far as I can see lazy loading images is still a relatively common pattern. On top of that, the profile of the average internet-connected device has changed drastically (under-powered Android devices on very expensive cellular connections) and in my opinion the argument for lazy loading images is stronger now than it was 5 years ago. I'm going to provide some insight into a use case that I'm very familiar with: the BBC News front page. I'll do this in the hopes that it provides some real life context around why I think lazy loading images is important, and why doing it in JS is not good for users. Loading the page in Firefox in a 360 x 640 viewport from the UK (important because the UK does not get ads, which skews the results), the browser makes the following requests:
We use lazysizes to lazy load all but the very first article image. Lazysizes makes up about half of our JS bundle size. I know it's overkill for our use case but it's a popular and well-tested library. We load our JS with a From our point of view the benefits of the UA providing lazy loading are:
Despite Ilya's arguments against lazy loading in general, we've been doing it for 5 years and we're going to continue doing it until cellular data is much cheaper. If we got rid of our lazy loading, two thirds of our mobile users would download 170kB of data that they never use. Keeping the next billion in mind, that's about 3 minutes of minimum wage work. At our scale (up to 50M unique mobile visitors to the site each week) 170kB per page load starts to feel uncomfortably expensive for our users. So what do the WHATWG folk think? Is it worth having this conversation again? Is there still vendor interest? Mozilla expressed interest 5 years ago but it seems like nothing really happened. |
Intersection observers means the JS for triggering loading on element visibility is tiny.
That's also possible with a small amount of JS.
Yeah I think browser heuristics (along with no JS dependency) are the remaining selling points of something like |
Yeah, fair call. If we drop our big ol' lazy loading JS for a I guess the thing that appeals to me most about a |
I can see two more arguments in favour of an attribute. The first is that lazy loading mechanisms which depend on scripts have a significant impact for user agents where scripts don't execute. To prevent images from loading early, the images are only inserted into the DOM later on, leaving non-scripting environments without images at all. Few sites seem to think about the The second is that providing it through an attribute means that the user can configure the behaviour as they prefer to experience the web. Someone on a slow connection might want to make images start loading earlier than when the image enters the viewport in order to finish loading in time, while someone else with a lot of bandwidth who dislikes lazy loading can disable it entirely. (In general, I believe it is important that common website practices are standardised in order to give some control of the experience back to the user, or we may eventually find ourselves with a web that is more of a closed runtime than a document platform which is open to changes by extensions, user scripts and userstyles.) |
@Zirro those arguments are the "browser heuristics" and "no JS dependency" benefits I already mentioned, no? |
@jakearchibald I suppose I understood the "no JS dependency" benefit as referring only to having to load less JavaScript rather than the content being available to non-scripting agents, and missed the meaning of "browser heuristics" in your sentence. Still, I hope that detailing the arguments and why they are important can help convince those who are not yet sure about why this would be useful. |
In general non-scripting agents are not a very compelling argument to get browsers to support a proposal, given that they all support scripting :). (And I believe these days you can't turn off scripting in any of them without extensions.) |
@domenic I would hope that they see the value in having a Web that is accessible to all kinds of agents beyond their own implementations, much like a person without a disability can see the value of designing a website with accessibility in mind. |
@domenic The issue is more whether these scripts fail to download, which does lead to an odd experience. It's becoming harder and harder these days to progressively enhance websites as we seem to depend on scripting more and more for the core functionality of our websites.
I think both of these are big selling points for the reasons above. As I say, this is not something that's possible to progressively enhance. There is not any way to provide a fallback for those for whom the JS fails for whatever reason. A few years ago, GDS calculated how many visits do not receive 'JavaScript enhancements', which was a staggering 1.1%. Like GDS, at the BBC, we have to cater to a very wide audience and not all of them will have stable internet connections. I have a good connection at home and even for me the lazyloading script can fail to kick in sometimes. Additionally, I feel as though we haven't covered one of the main issues with this that I mentioned in my original comment:
Because we're using a script, we've had to use placeholder |
I completely agree with this. At Wikipedia/Wikimedia, we have seen that interrupted JS downloads in low quality bandwidth situations are one of the most common causes of various problems. And that's also exactly the user situation where you'd want lazy loaded images. I'd guess with service workers you could do lazy loaded images as well, and then at least you're likely to have them on your second successful navigation, but yeah:
|
A topic I would like to tease apart is whether lazy-loading of images alone is the most compelling use-case to focus on vs. a solution that allows attribute-based priority specification for any type of resource (e.g I know |
It would definitely be useful to have this for As for When you mention a more general priority specification, do you mean something like the old Resource Priorities spec? What kind of behaviour are you thinking of? |
I believe Edge already does lazy image loading. For out-of-viewport images, it loads enough of the image to get metadata for size (with byte-range requests?), but not the entire image. The entire image is then loaded when visible to the user. Would lots of small byte-range requests for out-of-viewport images be acceptable? |
@mcmanus I think the previous comment in this thread is of interest to you. |
I have two questions:
I can't think of any use of these cases: async="on" and lazyload="off" If any of them is "on", the browser will be lazy loading or decoding the image. In any case, the user won't see the image drawn immediately. So should not a single attribute be used to indicate the laziness for loading and the decoding the image?
|
I guess that would be a separate discussion in the CSS WG, but at least in the case of BBC websites, the few background images that are used are visible at the top of the page, and therefore need to be loaded immediately anyway.
It also depends on if these attributes would prevent the image from being downloaded entirely, or whether it would just affect the order in which the images are downloaded. (I think the latter would be much less useful.) |
The content performance policy draft suggests |
For a complete proposal, we probably need not just a way to mark an image as lazy loading but also a way to provide a placeholder. Sometimes colors are used as placeholders but often it's a more data-compact form of the image itself (a blurry view of the main image colors seems popular). Placeholders are also sometimes used for non-lazy images, e.g. on Medium the immediately-visible splash images on articles briefly show a fuzzy placeholder. Also: Apple is interested in a feature along these lines. |
@othermaciej in early 2014 I proposed CSS |
The Chrome team's proposal is a lazyload=”” attribute. It applies to images and iframes for now, although in the future we might expand it to other resources like videos. “lazyload” has the following states:
In Chrome we plan to always respect on and off. (Perhaps we should make them always-respected in the spec too, instead of being strong hints? Thoughts welcome.) Deferring images and iframes delays their respective load events until they are loaded. However, a deferred image or iframe will not delay the document/window's load event. One possible strategy for lazyload="on", which allows lazily loading images without affecting layout, is to issue a range request for the beginning of an image file and to construct an appropriately sized placeholder using the dimensions found in the image header. This is the approach Chrome will take. Edge might already do something similar. We’re also open to the idea of supporting developer-provided placeholder images, though ideally, lazyloaded images would always be fully loaded before the user scrolls them into the viewport. Note that such placeholders might already be accomplishable today with a CSS background-image that is a data URL, but we can investigate in parallel with lazyload="" a dedicated feature like lowsrc="" or integration into Although we won’t go into the details here (unless you’re interested), we also would like to add a feature policy to flip the default for lazyload="" from auto to off. This would be used for example for a particularly important <iframe>, where you could do , which would disable all lazyloading within that frame and its descendants. |
@bengreenstein It is great to hear your proposal. I have a couple of questions:
Does this imply images will not be downloaded until visible in the viewport (at least on metered network connections)?
If width and height attributes are already provided by the author, will that negate the need for this request? |
Here's a number of thoughts on this proposal:
|
Some additional thoughts:
|
Good point. We need to consider |
@eeeps I've tested that, and it doesn't seem to always catch all images on the initial load in both Firefox and Chrome. Sometimes it does, but sometimes the browser seems to just download a couple of images regardless, even if the I very much wish we had a way to tell the browser to delay loading images without having to remove the |
That's the reason. |
I think these are mostly things the browser knows best (slow/fast network connection, is user scrolling fast?) and should decide itself. |
@bengreenstein with I'm just wondering what <img src="#" lazyload />
<img src="#" lazyload="on" /> If the three states are required I think @othermaciej's suggestion makes the most sense
Where missing and invalid values default to Also, this leaves space for more functionality over time like |
@othermaciej I'm also curious about the use within I agree with @Link2Twenty, that if we can't do a boolean attribute, I prefer @othermaciej's states.
|
@Link2Twenty @stramel I also still agree with myself! I don't think a good justification has been given for the on/off/auto tristate. It is generally a confusing pattern in API design instead of having named states. And we will regret on/off/auto badly if there is ever a fourth state. Just to raise some crazy hypotheticals: what if there was a "manual" state that would only load when explicitly asked via a DOM API? Or maybe a state that would eagerly load only the metadata? In fact I don't think any of the points of feedback from my April 19, 2018 comment have been addressed. |
As author of one of the most used LazyLoad scripts, I advise we consider these two cases. Some of the script users ask for the ability to load the images via API, and one of the most difficult things when using lazily loaded images is to make them occupy the right space so eagerly fetch the meta data would be a great idea! So I agree with @othermaciej |
Re-reviewing the thread, I'm a fan of @othermaciej's tristates ( Looking at how lazy-loading responsive images has been tackled in userland, Lazysizes appears to define lazy-loading behavior on <picture>
<source
data-srcset="500.jpg"
media="(max-width: 500px)" />
<source
data-srcset="1024.jpg"
media="(max-width: 1024px)" />
<source
data-srcset="1200.jpg" />
<img
src="data:image/gif;base64,R0lGODlhAQABA"
data-src="1024.jpg"
class="lazyload"
alt="image with artdirection" />
</picture> I wonder if our equivalent would be something akin to this: <picture>
<source
srcset="500.jpg"
media="(max-width: 500px)" />
<source
srcset="1024.jpg"
media="(max-width: 1024px)" />
<source
srcset="1200.jpg" />
<img
src="1024.jpg"
load=lazy
alt="image with artdirection" />
</picture> |
From a privacy perspective, does this feature give the ability for a host to embed lazy loaded images to determine what parts of a page a user is scrolling to even if they have script turned off? I feel like there is a case for flagging how this can be misused, if that discussion hasn't already taken place. Perhaps browser vendors could disable this in their private browsing modes, but maybe that is out of spec for this issue. |
It would probably be a good idea to mention that privacy consideration. Of course, it applies only to the script disabled scenario, because otherwise, Intersection Observer provides the info directly. It's also not exact, because loading has to be started some time before the user scrolls to a particular point, and this may vary between browsers and devices. |
Aside from the privacy issue mentioned by @mstancombe there's another angle that should be considered here: |
@wolfbeast a few comments: 1. Does this not improve the situation for the ad industry? They don’t want to be recording impressions for ads that aren’t actually seen, right? Presumably at the moment, they are factoring in that a percent of ads will be loaded, recorded as an impression but the user never scrolled to see it, therefore the CPMs are artificially lowered. If lazy loading is adopted, reporting would be more accurate and CPMs could increase to cover gap with the lower volumes. 2. If the above is not valid, couldn’t you just set the value to |
@mstancombe I am happy to see privacy considered here, i would expect this to be discussed earlier but you're the first one to mention it as far as i can see. @othermaciej I agree that this is only applicable in an environment where javascript is disabled. I can't find any recent numbers about the total amount, but i am among the approximately 1 percent of people that browse without javascript enabled regularly for privacy considerations.
|
@PrinsFrank Can't you no-JS fingerprint viewport size right now with something like this? |
@Sora2455 I hadn't even considered that, but you're right! |
@ryantownsend re:
|
Is there an event or way to determine when a "lazy img" has started to load its source? |
@stramel I believe the answer is no, just as you cannot tell when a normal img begins to load its source (but I could be missing something?) |
@stramel @domfarolino can't we use Resource Timing for that? |
I guess you probably could.. |
I'd probably be non the wiser reading the spec so I'll just ask, is there anything for UAs to consider when authors both |
My mental model of what will happen:
I think that makes sense? And has, at worst, a few frames' worth of penalty vs using |
Thanks @eeeps!
I've asked Lighthouse to consider the scenario in GoogleChrome/lighthouse#9516, so that (if the audit is implemented) users who run site audits will be flagged for indecisive loading. But, perhaps it'd be wise(r) if the spec would mention it, asking the UA with a "SHOULD", or "MAY", to notify developers with a console warning in these cases? |
@eeeps that's indeed what I'd expect to happen. FWIW, Chromium and WebKit are already likely to show console warnings in those cases, if the image wasn't actually used a few seconds after onload. |
The only issue is that it is not possible spec-wise today. We'd need a way to know whether a lazy-loaded image's response was served from the preload cache or not, and if so display the warning you mention. (Currently the Fetch Standard does not know about a preload cache). With that said, I am comfortable with the Chromium/WebKit warnings issued when preload requests are not used. Would be interesting to see what lighthouse could come up with though. |
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 adiv
which acts as a placeholder. Thediv
is styled with CSS to have the same dimensions as the loaded image and has a grey background with a BBC logo on it.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:
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 thehead
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.
The text was updated successfully, but these errors were encountered: