-1

If I make a loop to dynamically add elements that do a transition on the transform , why does this example not work:

function changeSize() {
   for (let x = 0; x < 99; x++) {
      let el = document.createElement('div');
      el.style.translate = 'transformX(0px)';

      el.style.translate = 'transformX(150px)';
      el.style.transition = 'all 2s ease';
      
      let body = document.body;
      body.appendChild(el);
   }
}

but this does work as long as I add even a really small timeout:

function changeSize() {
   for (let x = 0; x < 99; x++) {
      let el = document.createElement('div');
      el.style.translate = 'transformX(0px)';

      // now it will transition 
      setTimeout(() => {
         el.style.translate = 'transformX(150px)';
         el.style.transition = 'all 2s ease';
      }, 50)

      let body = document.body;
      body.appendChild(el);
   }
}
RicardoAlvveroa
  • 226
  • 1
  • 8

1 Answers1

1

When you set transform and then reset it within the same thread, it just changes the value and takes on the later value. The setTimeout brings everything to a different thread on the browser level (not the javascript level, because js is single-threaded), causing the animation to occur when the value is changed. However, javascript like this is often unpredictable in different browsers - rather, use a css to make such transitions.

function changeSize() {
  for (let x = 0; x < 99; x++) {
    let el = document.createElement("div");
    el.innerHTML = "hi"
    el.style.transition = "all 2s ease";
    setTimeout(function() {
      el.classList.add("transition");
    }, 1)  /*Brings everything to a different thread.
When the browser sees this, it's like - oh look, a setTimeout, I need to move this to a time thread,
so when the time completes I can execute the function! So it does that.
Since the time is very little, it completes within a short amount of time.
The browser sees this, and realizes that it needs to execute the function - so it removes it from it's
execution stack and executes it.
When executing, it sees that the thread is asking for a classList change for the element el.
So, it changes the class, triggering any css with it (in this case the transform css).
Since the element has a transition, it appropriately transitions during the translation.
*/
    document.body.appendChild(el);
  }
}
changeSize()
.transition {
  transform: translate(100px, 0);
}

Note the value 1 for the setTimeout, because some browsers don't wait for the value 0 (as in move it to a separate stack).

Now, you might be wondering, why is this 'unpredictable'? Well, this is because with such a short timeout period, it can execute along with other code in the main thread. If you have code in the main thread that changes the element's css, then the browser will have a clash, and will do different things depending on which one gets executed first. However, if you use a class and then change the element's style itself later on, the style will override the class since it is applied to the element directly rather than through the means of a class.

Endothermic_Dragon
  • 1,147
  • 3
  • 13
  • thanks. In your code, why does adding a line before the setTimeout to give it a starting point not work, like : `el.style.transform = translate(0px, 0px)` and then in the setTimeout below it, add the class that gives it a different translate. – RicardoAlvveroa Jan 08 '21 at 17:30
  • That's because when you do that, the el element itself gets styled, which overrides the class. This is the exact thing that prevents css issues in later code by allowing later code to override it, and in this case, that's what it does :). – Endothermic_Dragon Jan 08 '21 at 22:46
  • _facepalms self_ didn't notice that, thank you!! – RicardoAlvveroa Jan 10 '21 at 21:17