We had two issues implementing lazysizes in our site:
position: fixed background <img> tags that are contained by a parent with overflow: hidden. Lazysizes thinks these are always visible because their offsetTop is always zero, despite not _actually_ being visible for several thousands pixels down the page. _(FYI, we originally had CSS background images, but ran into several issue requiring us to change to <img/> tags)_<img> elements that aren't always visible, and get dynamically shown with JS. Lazysizes doesn't load these hidden images until their shown by our JS, which is too late.I've gotten around both of these issue by writing a small library called lazyload-children. It allows you to watch for the visibility of a parent element, and when it gets close enough to the viewport, it will load the specified children.
Add data-lazyload-children attribute on the parent container to watch, and the value is a CSS selector matching the children to be lazyloaded.
<div class="some-container-element" data-lazyload-children=".dynamic-images">
<p>Dynamic images example</p>
<img data-src="./media/example1.jpg" class="dynamic-images" style="display: none">
<img data-src="./media/example2.jpg" class="lazyload" alt="Loaded normally via lazysizes">
<p>These images were lazyloaded once their parent element was close to the viewport</p>
<img data-src="./media/example3.jpg" class="dynamic-images" style="display: none">
</div>
Is there any better solutions inside lazySizes to handle these scenarios? If not, should I formalise the plugin and submit a PR?
About 1:
FYI, we originally had CSS background images, but ran into several issue requiring us to change to
tags
I assume perf problems while scrolling, especially in Chrome?
lazysizes should detect here, that the image is not really in view, but fals-y assumes later that it is soon in view. Which means the images should not be loaded, while the window is still loading after that, lazysizes will load them in a queue of max 3 images at the same time. If these images are loaded earlier, it is a bug.
About 2:
Yes, if you hide images or the container of them via display: none;, you remove them from the render tree and lazysizes can't detect where there are. Another possible solution is to use visibility: hidden; in combination with position: absolute; and/or height: 0; overflow: hidden;. This also gives you the possibility to easily animate them via CSS transitions.
All this said: Yes, you script is a valuable workaround/fix for those kind of problems. If you want full control, you can also host them yourself and just add a link to my repository. If you don't need/want full control over the plugin you can request a PR with the full code into the master branch.
I assume perf problems while scrolling, especially in Chrome?
Yes that, but mainly because background-attachment: fixed doesn't work on mobile. And much harder to do LQIP.
lazysizes should detect here, that the image is not really in view, but fals-y assumes later that it is soon in view. Which means the images should not be loaded, while the window is still loading after that, lazysizes will load them in a queue of max 3 images at the same time. If these images are loaded earlier, it is a bug.
All fixed <img> elements were being loaded upfront (like they weren't lazy at all). Not sure how lazysizes does visibility calculation, but if it's calculated from the .lazyload element's offsetTop/getBoundingClientRect().top, that's always 0 for fixed elements (assuming you've got top: 0 in your CSS, which I do).
Yes, if you hide images or the container of them via display: none;, you remove them from the render tree and lazysizes can't detect where there are. Another possible solution is to use visibility: hidden; in combination with position: absolute; and/or height: 0; overflow: hidden;. This also gives you the possibility to easily animate them via CSS transitions.
Yes, was considering tweaking our code so that they weren't display: none, but realised my original fix for the fixed backgrounds would solve this too!
My code will need a bit of work to make it an actual plugin because it doesn't use/respect any of lazySizes config and does it's own visibility detection. I've made a gist if you want to check it out.
About your code:
(function(){
'use strict';
var slice = [].slice;
document.addEventListener('lazybeforeunveil', function(e){
var children = e.target.getAttribute('data-lazyload-children');
if(children){
slice.call(e.target.querySelectorAll(children)).forEach(lazySizes.loader.unveil);
}
});
})();
Should work with:
<div class="some-container-element lazyload" data-lazyload-children=".dynamic-images">
<p>Dynamic images example</p>
<img data-src="./media/example1.jpg" class="dynamic-images" style="display: none">
<img data-src="./media/example2.jpg" class="lazyload" alt="Loaded normally via lazysizes">
<p>These images were lazyloaded once their parent element was close to the viewport</p>
<img data-src="./media/example3.jpg" class="dynamic-images" style="display: none">
</div>
Note: I just added the class lazyload to your div.
Most helpful comment
About your code:
Should work with:
Note: I just added the class
lazyloadto yourdiv.