Skip to content Skip to sidebar Skip to footer

Cancel Pending SetTimeouts For Multiple Items In For Loop

I'm trying to create a cool little micro interaction, but I'm running into a minor issue. If you toggle in too quick of a succession, some items will ultimately be displayed, wher

Solution 1:

I would simplify your logic and consider transition-delay where you only need to toggle a class. The trick is to have a different delay for your elements when we toggle the class to have the desired effect.

With this configuration you won't have any issue because all the element will have the same state since the class is added to their parent element.

var nav = document.querySelector('nav');
document.querySelector('button').onclick = function(){
  nav.classList.toggle('top');
}
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
  --d:0.2s;
}

nav > a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
nav.top > a {
  transform: translateY(0);
}

nav > a:nth-last-child(1) { transition-delay:calc(0 * var(--d));}
nav > a:nth-last-child(2) { transition-delay:calc(1 * var(--d));}
nav > a:nth-last-child(3) { transition-delay:calc(2 * var(--d));}  
nav > a:nth-last-child(4) { transition-delay:calc(3 * var(--d));}

nav.top > a:nth-child(1) { transition-delay:calc(0 * var(--d));}
nav.top > a:nth-child(2) { transition-delay:calc(1 * var(--d));}
nav.top > a:nth-child(3) { transition-delay:calc(2 * var(--d));}  
nav.top > a:nth-child(4) { transition-delay:calc(3 * var(--d));}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>

We can simplify the CSS code by grouping the elements with the same delay:

var nav = document.querySelector('nav');
document.querySelector('button').onclick = function(){
  nav.classList.toggle('top');
}
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
  --d:0.2s;
}

nav > a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
nav.top > a {
  transform: translateY(0);
}

nav > a:nth-last-child(1),
nav.top > a:nth-child(1) { transition-delay:calc(0 * var(--d));}

nav > a:nth-last-child(2),
nav.top > a:nth-child(2) { transition-delay:calc(1 * var(--d));}

nav > a:nth-last-child(3),
nav.top > a:nth-child(3){ transition-delay:calc(2 * var(--d));}  

nav > a:nth-last-child(4),
nav.top > a:nth-child(4){ transition-delay:calc(3 * var(--d));}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>

Solution 2:

This anwer shows a good way to clear all setTimeout - just add it into each part of the if/else statement:

document.querySelector('button').onclick = function() {
    const
      items = document.querySelector('nav').children

    if (items[0].getBoundingClientRect().top >= document.querySelector('nav').getBoundingClientRect().bottom) {
      var id = window.setTimeout(() => {}, 0);
      while (id--) {
        window.clearTimeout(id);
      }
      // start showing elements, starting from the beginning
      for (let i = 0; i < items.length; i++) {
        setTimeout(function() {
          items[i].style.transform = 'translateY(0)'
        }, i * 200)
      }
    } else {
      var id = window.setTimeout(() => {}, 0);
      while (id--) {
        window.clearTimeout(id);
      }
        // start hiding elements, starting from the back
        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>

Post a Comment for "Cancel Pending SetTimeouts For Multiple Items In For Loop"