I'm trying to create a cool little micro interaction, but I'm running into a minor issue.
document.querySelector('button').onclick = function(){
const
items = document.querySelector('nav').children
if (items[0].getBoundingClientRect().top >= document.querySelector('nav').getBoundingClientRect().bottom){
// start showing elements, starting from the left side
for (let i = 0; i < items.length; i++){
setTimeout(function(){
items[i].style.transform = 'translateY(0)'
}, i * 200)
}
} else {
// start hiding elements, starting from the right side
for (let i = 0; i < items.length; i++){
setTimeout(function(){
items[i].style.transform = 'translateY(100%)'
}, (items.length-1 - i) * 200)
}
}
}
button {
width: 100px;
height: 50px;
}
nav {
width: 50vw;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 10px;
background: red;
}
nav > a {
width: 100%;
height: 50px;
transition: .5s transform;
transform: translateY(100%);
opacity: 0.5;
background: lime;
}
<button>Toggle</button>
<nav>
<a href=''></a>
<a href=''></a>
<a href=''></a>
<a href=''></a>
</nav>
If you toggle in too quick of a succession, some items will ultimately be displayed, whereas others will ultimately be hidden.
This is due to the fact that there are pending setTimeouts
that have yet to be executed when the new set of setTimeouts
are issued.
Obviously there are ways around this issue, like not reversing the order of the animation, waiting until the animation is completely finished before allowing the reverse, et cetera, but I would rather not make such compromises.
I've tried using and toggling a global Boolean in the if
and else
blocks, and then using an additional if/else
statement in the setTimeout
block, but this didn't work.
I also tried setting transition
delays on the fly before applying the new transform
values, instead of relying on setTimeout
, which didn't work.
Is there a simple way to cancel or ignore any pending setTimeouts
from the older cycle?