UPDATE: the solution I have posted below is not good enough, because it makes all the bullets except the active one non-responsive to clicks, instead of queueing them, so there is room for improvement.
I am working on a custom image carousel, using jQuery and CSS. My aim is to make it really lightweight but with (just) enough features: "bullets", auto-advance, responsiveness.
It works fine, but I have discovered a bug I was unable to fix: when I click 2 bullets in rapid succession - which means clicking the second before the transition triggered by the first is finished - the transitions overlap in a weird manner I can not describe but is visible below:
var $elm = $('.slider'),
$slidesContainer = $elm.find('.slider-container'),
slides = $slidesContainer.children('a'),
slidesCount = slides.length,
slideHeight = $(slides[0]).find('img').outerHeight(false),
animationspeed = 1500,
animationInterval = 7000;
// Set (initial) z-index for each slide
var setZindex = function() {
for (var i = 0; i < slidesCount; i++) {
$(slides[i]).css('z-index', slidesCount - i);
}
};
setZindex();
var displayImageBeforeClick = null;
var setActiveSlide = function() {
$(slides).removeClass('active');
$(slides[activeIdx]).addClass('active');
};
var advanceFunc = function() {
if ($('.slider-nav li.activeSlide').index() + 1 != $('.slider-nav li').length) {
$('.slider-nav li.activeSlide').next().find('a').trigger('click');
} else {
$('.slider-nav li:first').find('a').trigger('click');
}
}
var autoAdvance = setInterval(advanceFunc, animationInterval);
//Set slide height
$(slides).css('height', slideHeight);
// Append bullets
if (slidesCount > 1) {
/* Prepend the slider navigation to the slider
if there are at least 2 slides */
$elm.prepend('<ul class="slider-nav"></ul>');
// make a bullet for each slide
for (var i = 0; i < slidesCount; i++) {
var bullets = '<li><a href="#">' + i + '</a></li>';
if (i == 0) {
// active bullet
var bullets = '<li class="activeSlide"><a href="#">' + i + '</a></li>';
// active slide
$(slides[0]).addClass('active');
}
$('.slider-nav').append(bullets);
}
};
var slideUpDown = function() {
// set top property for all the slides
$(slides).not(displayImageBeforeClick).css('top', slideHeight);
// then animate to the next slide
$(slides[activeIdx]).animate({
'top': 0
}, animationspeed);
$(displayImageBeforeClick).animate({
'top': "-100%"
}, animationspeed);
};
$('.slider-nav a').on('click', function(event) {
displayImageBeforeClick = $(".slider-container .active");
activeIdx = $(this).text();
if ($(slides[activeIdx]).hasClass("active")) {
return false;
}
$('.slider-nav a').closest('li').removeClass('activeSlide');
$(this).closest('li').addClass('activeSlide');
// Reset autoadvance if user clicks bullet
if (event.originalEvent !== undefined) {
clearInterval(autoAdvance);
autoAdvance = setInterval(advanceFunc, animationInterval);
}
setActiveSlide();
slideUpDown();
});
body * {
box-sizing: border-box;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.slider {
width: 100%;
height: 300px;
position: relative;
overflow: hidden;
}
.slider .slider-nav {
text-align: center;
position: absolute;
padding: 0;
margin: 0;
left: 10px;
right: 10px;
bottom: 2px;
z-index: 30;
}
.slider .slider-nav li {
display: inline-block;
width: 20px;
height: 3px;
margin: 0 1px;
text-indent: -9999px;
overflow: hidden;
background-color: rgba(255, 255, 255, .5);
}
.slider .slider-nav a {
display: block;
height: 3px;
line-height: 3px;
}
.slider .slider-nav li.activeSlide {
background: #fff;
}
.slider .slider-nav li.activeSlide a {
display: none;
}
.slider .slider-container {
width: 100%;
text-align: center;
}
.slider .slider-container a {
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
}
.slider .slider-container img {
transform: translateX(-50%);
margin-left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="container">
<div class="slider slider-homepage">
<div class="slider-container">
<a href="#">
<img src="https://picsum.photos/1200/300/?gravity=east" alt="">
</a>
<a href="#">
<img src="https://picsum.photos/1200/300/?gravity=south" alt="">
</a>
<a href="#">
<img src="https://picsum.photos/1200/300/?gravity=west" alt="">
</a>
</div>
</div>
</div>
How could I prevent this phenomenon I would call, for lack of a better term, an event crowding (overlap)?
You can chain your animations using jQuery deferred object and Promise. Here is the class allowing you to do it easily.
The fiddle is with the animations queued.
PS: I increase the size of the buttons to click more easily
Here is a possible fix, consisting of waiting for an animation to finish before starting another:
You could use a queue: every time you click a bullet it add to the queue and execute the queue. Something like this:
I haven't tested this, so...
While this not strictly answering your question, one way you could solve your actual problem is by handling your slider differently, consisting of moving the slider instead of the slides :
1) Make the slides container absolute, and it's container relative
2) Instead of positionning the slides on click, move the slider container to the right position
This way no image will ever overlap.
Here is the fiddle, the code can certainly be refactored but I didn't have too much time to look into it, will try to post a snippet here asap if you're ok with not leaving your original thing of moving slides instead of the slider.