RicardoAlvveroa
RicardoAlvveroa

Reputation: 246

javascript: using transitionend and setTimeout to coordinate a loop?

How can I time this loop better? I am trying to go through all the div's in the DOM and transform them over 5s and then remove them. I don't think I am using the transitionend event properly. I just want to transform one element at a time, remove it and then continue the loop

let els = document.querySelectorAll('div');


els.forEach((x,i) => {
    setTimeout(() => {
       x.style.transform = 'translate(400%, 400%)';
       x.style.transition = 'all 5s ease-in';
       x.addEventListener('transitionend', () => {
         console.log("transition has ended");
         x.remove()
       });
    }, i * 400); 
})

Upvotes: 0

Views: 167

Answers (2)

Mister Jojo
Mister Jojo

Reputation: 22365

I don't see the point of setTimeOut usage, is it just for launching the transitions?
In this case I prefer to use a button!

let divs   = document.querySelectorAll('div')
  , btTest = document.querySelector('button')

const elmTransEnd = el => new Promise(rsvl =>
  {
  const endTrans=e=>{ el.removeEventListener('transitionend', endTrans);rsvl() }
  el.addEventListener('transitionend', endTrans);
  el.style.transition = 'all 3s ease-in';  
  el.style.transform = 'translateX(80%)';
  });
 
async function* stepGen(elArr) { yield* elArr; }

async function launchTransitions()
  {
  for await (let el of stepGen([...divs]))
    {
    await elmTransEnd(el);
    console.log('el', el.textContent, 'done')
    el.remove()
    }
  }
  
btTest.onclick = _ => 
  {
  btTest.disabled = true
  launchTransitions()
  }
<button>test</button>

<div>foo1</div>
<div>foo2</div>
<div>foo3</div>

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 371049

Since the transition lasts 5 seconds, multiply the i by 5000, not 400:

let els = document.querySelectorAll('div');


els.forEach((x,i) => {
    setTimeout(() => {
       x.style.transform = 'translate(400%, 400%)';
       x.style.transition = 'all 5s ease-in';
       x.addEventListener('transitionend', () => {
         console.log("transition has ended");
         x.remove()
       });
    }, i * 5000); 
})
<div>foo</div>
<div>foo</div>
<div>foo</div>

A more general approach without knowing the transition duration in advance would be to await a Promise:

let els = document.querySelectorAll('div');

setTimeout(() => {
  (async () => {
    for (const x of els) {
      await new Promise((resolve) => {
         x.style.transition = 'all 5s ease-in';
         x.style.transform = 'translate(400%, 400%)';
         x.addEventListener('transitionend', () => {
           console.log("transition has ended");
           x.remove()
           resolve();
         });
      });
    }
  })();
});
<div>foo</div>
<div>foo</div>
<div>foo</div>

Upvotes: 2

Related Questions