verlok / vanilla-lazyload

LazyLoad is a lightweight, flexible script that speeds up your website by deferring the loading of your below-the-fold images, backgrounds, videos, iframes and scripts to when they will enter the viewport. Written in plain "vanilla" JavaScript, it leverages IntersectionObserver, supports responsive images and enables native lazy loading.
https://www.andreaverlicchi.eu/vanilla-lazyload/
MIT License
7.66k stars 675 forks source link

loads element without any scroll. ignores threshold #591

Open buildpath-ian opened 1 year ago

buildpath-ian commented 1 year ago

Hi,

Please see the issue shown on video here: https://www.loom.com/share/d172a32f5f6a447a99ecd2942a9daeb9

My init is:

ll = new LazyLoad({ // Assign the callbacks defined above callback_enter: callback_enter, callback_exit: callback_exit, callback_cancel: callback_cancel, callback_loading: callback_loading, callback_loaded: callback_loaded, callback_error: callback_error, callback_finish: callback_finish, elements_selector: "iframe", threshold: 300, });

I also have the callbacks from your demos set up, so I can see that it's loading without me scrolling at all. It seems to do it twice somehow. I don't know what to think except that something on my page may be tricking the Intersection Observer somehow.

MtDalPizzol commented 10 months ago

I'm also facing this issue. threshold seems to simply be ignored, which leads to <iframe> that are close to the fold to be loaded directly on the first page load, which causes Core Web Vitals TBT problems since a lot of third-party code ends up being downloaded without any user interaction.

MtDalPizzol commented 10 months ago

Well... it seems that the problem was use_native. You can see here that browser's native thresholds are pretty high (1250px). The solution I found was to use native loading only for images and use the library for iframes to avoid loading tons of third-party code.

verlok commented 10 months ago

Thanks, @MtDalPizzol it makes sense.

Yes it you set use_native you then delegate the lazy loading to the browser, losing most control on the loading via JavaScript.

The solution you found sounds very smart. Can I ask you to try using JS (not native) lazy loading also on the images and see if your Largest Contentful Paint further improves? I guess it will.

MtDalPizzol commented 10 months ago

@verlok I could try that. But to be honest, I don't see how it would make LCP better. I feel like anything that needs "foreign" JS code will always perform worse than native browser functionality. Is there something I'm not aware of here? Also, in the Core Web Vitals docs, they repeatedly state that you should run as little JS as possible AND use native when available. On top of that, my largest element on page load is not lazily loaded. So, at least in my case, it doesn't seem necessary to do that.

MtDalPizzol commented 10 months ago

By the way, to be clear: I'm using the library for both (images, and iframes), just with different configs:

import LazyLoad from 'vanilla-lazyload'

export default function initLazyLoad () {
  const lazyImages = new LazyLoad({
    elements_selector: 'img.lazy',
    use_native: true
  });

  const lazyIframes = new LazyLoad({
    elements_selector: 'iframe.lazy',
    threshold: 600
  });
}
verlok commented 10 months ago

@MtDalPizzol

But to be honest, I don't see how it would make LCP better.

I will explain in a second..

I feel like anything that needs "foreign" JS code will always perform worse than native browser functionality.

Correct.

On top of that, my largest element on page load is not lazily loaded.

Awesome. This is indeed a best practice.

If you take all of the a above, you may agree with me that a) the image causing your LCP won't be delayed if you load every other image else using JS, but b) if you use native lazy loading, images that are just below the fold are loaded as soon as the page loads, meaning while the LCP image is loading, meaning slowing its loading down.

This would be visible especially on slower connection.

You don't have trust me, just try it and test it using webpagetest.org, on both fast and slow connections.

MtDalPizzol commented 10 months ago

@verlok

b) if you use native lazy loading, images that are just below the fold are loaded as soon as the page loads, meaning while the LCP image is loading, meaning slowing its loading down.

This makes A LOT of sense. I'll surely give this a try. Thank you so much for the tip.

My only concern with this, is that Core Web Vitals are a tricky beast to handle. We're working hard, trying to get ready for the new INP metric that comes into play in march 2024, and since TBT can affect INP, I'm a little worried that leaving image handling to JS might affect INP due to potentially triggering long tasks during the page lifecycle.

verlok commented 10 months ago

Thanks for sharing, @MtDalPizzol I'm a web performance consultant (check andreaverlicchi.eu) so I perfectly understand what you say.

I cN tell you that my lazy load script doesn't run any long task, it just watches the elements using the IntersectionObserver API and when it's time, it copies one attribute on another.

MtDalPizzol commented 10 months ago

@verlok

I cN tell you that my lazy load script doesn't run any long task, it just watches the elements using the IntersectionObserver API and when it's time, it copies one attribute on another.

Sure! In fact, that was the reason I switched from lazysizes to vanilla-lazyload. lazysizes was triggering long tasks when I inspected my pages on the Performance tab.

I'll go ahead and implement your suggestions! Thanks a lot, again!

PS: already following on X and reading the articles on the site. :relaxed: