This question is a more specific and a superset of this question: How to know if window "load" event was fired already .
Use Case
So I have this use case where I need to make a lazy loaded CSS-only slideshow of pictures and for that I realized it thru onload Events which fire with the slide number, which fires after a delay only when the picture finished loading the picture in question. The problem is that the suggested answer shows 'complete' after the first onload Event fired, which is not fitting for me. So my question is - how do I detect whether all onload Events already fired?
The problem
All my slides go thru and afterwards the slideshow misbehaves. That's why I need to detect that condition so that I can control my slide behaviour after the fact and so fix this problem.
StackBlitz Link
https://stackblitz.com/edit/js-fsymew
GitHub Link
https://github.com/munchkindev/js-fsymew
My HTML:
<div style="margin-bottom: 30px;">
<img class="slider" src="graphics/slider1.webp" onload="delayedCarousel(1);">
<div class="slider-text" id="slidert1">Text here, you can just ignore this.</div>
<img loading="lazy" class="slider" src="graphics/slider2.webp" onload="delayedCarousel(2);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider3.webp" onload="delayedCarousel(3);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider4.webp" onload="delayedCarousel(4);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider5.webp" onload="delayedCarousel(5);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider6.webp" onload="delayedCarousel(1);" style="display:none">
<button class="slider-button slider-black slider-display-left" onclick="changeSlide(-1);">❮</button>
<button class="slider-button slider-black slider-display-right" onclick="changeSlide(1);">❯</button>
</div>
JS:
function carousel(n) {
var i;
var x = document.getElementsByClassName("slider");
if (n === 6) { delayedCarousel(1); }
//for (i = 0; i < x.length; i++) {
x[n].style.display = "block";
if (n === 1) x[5].style.display = "none";
//}
slideIndex++;
//if (n > x.length) {/*slideIndex = 1*/
x[n-1].style.display = "none";
//setTimeout(carousel, 5000); // Change image every 5 seconds
}
function delayedCarousel(n) {
setTimeout(function(){
carousel(n);
}, 3000);
}
function changeSlide(n) {
showDivs(slideIndex += n);
}
function showDivs(n) {
var i;
var x = document.getElementsByClassName("slider");
if (n > x.length) { slideIndex = 1 }
if (n < 1) { slideIndex = x.length }
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
x[i].style.width = "100%";
}
x[slideIndex - 1].style.display = "block";
}
Try 2
HTML:
<div style="margin-bottom: 30px;">
<img class="slider" src="graphics/slider1.webp" onload="delayedCarousel(1);">
<div class="slider-text" id="slidert1">Ignore this text, it's irrelevant.</div>
<img loading="lazy" class="slider" src="graphics/slider2.webp" onload="delayedCarousel(2);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider3.webp" onload="delayedCarousel(3);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider4.webp" onload="delayedCarousel(4);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider5.webp" onload="delayedCarousel(5);" style="display:none">
<img loading="lazy" class="slider" src="graphics/slider6.webp" onload="loopedCarousel(1);" style="display:none">
<button class="slider-button slider-black slider-display-left" onclick="changeSlide(-1);">❮</button>
<button class="slider-button slider-black slider-display-right" onclick="changeSlide(1);">❯</button>
</div>
JS:
// function startSliderLoop() {
// var i = 1;
// setInterval(function() {
// location.href = "/#slide-"+i;
// //document.addEventListener('click', function (event) {
// // If the clicked element does not have and is not contained by an element with the .click-me class, ignore it
// //if (!event.target.closest('#slideButton'+ i )) return;
// // Otherwise, do something...
// if (i <= 5)
// i++;
// else
// i = 1;
//}, 3000)
// }
// document.addEventListener("DOMContentLoaded", (event) => {
// startSliderLoop();
//});
var slideIndex = 0;
var sliders = document.querySelector('.slider');
//document.querySelector('.slider').forEach(function(img){
// img.addEventListener('load', carousel());
// });
function carousel(n) {
var i;
var x = document.getElementsByClassName("slider");
if (n === 6) { delayedCarousel(1); }
//for (i = 0; i < x.length; i++) {
x[n].style.display = "block";
if (n === 1) x[5].style.display = "none";
//}
slideIndex++;
//if (n > x.length) {/*slideIndex = 1*/
x[n-1].style.display = "none";
//setTimeout(carousel, 5000); // Change image every 5 seconds
}
function loopedCarousel(n) {
var i;
var x = document.getElementsByClassName("slider");
if (n === 5) { n=1; loopedCarousel(1); }
//for (i = 0; i < x.length; i++) {
x[n].style.display = "block";
if (n === 1) x[5].style.display = "none";
//}
slideIndex++;
//if (n > x.length) {/*slideIndex = 1*/
x[n-1].style.display = "none";
setTimeout(loopedCarousel(n+1), 5000); // Change image every 5 seconds
}
function delayedCarousel(n) {
setTimeout(function(){
carousel(n);
}, 3000);
}
I would use promises to listen to image load events, this way you can wait for all the promises to complete before initiating your carousel.
For example, if you had the below html:
You could wait for all these to load with something like:
I've created working example for you below. You'll notice that before all the images have loaded the carousel will display a red border. Once loaded the border color changes to green.
Or you can view it on JSFiddle here: https://jsfiddle.net/thelevicole/b13gpyfd/2/
Edit
Below is updated example of how to lazy load slide images only when the slide is in view instead of waiting for all images to load on init.
https://jsfiddle.net/thelevicole/b13gpyfd/3/
I've added a hook mechanism to allow callbacks to run everytime the slide changes
carousel.addAction('goto', (slide, index) => { ... });