0

I want to do the following: Create a div (#test) and then clone that div many times, each time it is cloned adding a css transition to it via javascript. All works well the first time, but if I try to clone a second time, and apply the css transition, then the transition doesn't work.

In this example (https://jsfiddle.net/9uL1qt6n/13/), the red square moves like it is supposed to, but the green square doesn't move, and appears at the end of the transition instantly.

Here is the javascript code that I am using:

function move(color){
  let clone=document.getElementById("test").cloneNode(true);
  clone.id=color;
  clone.style.display="block";
  clone.style.backgroundColor=color;
  document.getElementById("main").prepend(clone);
  setTimeout(function(){
      clone.style.left="500px";
  },0)
}

setTimeout(function(){move("red")},500);
setTimeout(function(){move("green")},750);

I am expecting the red square to start with left=0px at .5s and move to the right, and then a green square that starts with left=0px at .75s and move to the right. What I am seeing is a red square that starts with left=0px at .5s and moves to the right, and then a green square that starts with left=500px at .75s and does not move.

Edit: This appears to work correctly on Safari on Mac, as well as on Safari and Chrome in iOS. The above suggested behavior only appears on Chrome on Mac.

user1763510
  • 1,070
  • 1
  • 15
  • 28

1 Answers1

1

This is because setTimeout(/**/, 0) does not guarantee that the callback will be executed on a subsequent frame. Which could (depending on browser implementation and computer speed) result in the style being applied on the same frame as the node being inserted into the DOM.

In theory, you should use requestAnimationFrame instead, which is exactly meant for this type of situations.

However, in the fiddle you linked, it only worked if I doubled the requestAnimationFrame which is imperceptible but still undesirable... IDK if it's a fluke of JSFiddle or what...

function move(color){
  let clone=document.getElementById("test").cloneNode(true);
  clone.id=color;
  clone.style.display="block";
  clone.style.backgroundColor=color;
  document.getElementById("main").prepend(clone);
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
        clone.style.left="500px";
    })
  })
}

here's a snippet: I find the same thing in the SO snippet

function move(color) {
  let clone = document.getElementById("test").cloneNode(true);
  clone.id = color;
  clone.style.display = "block";
  clone.style.backgroundColor = color;
  document.getElementById("main").prepend(clone);
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      clone.style.left = "500px";
    })
  })
}


setTimeout(() => move("red"), 500);
setTimeout(() => move("green"), 750);
#main {
  display: block;
  width: 100vw;
  height: 100vh;
  background-color: blue;
}

.test {
  position: absolute;
  display: none;
  width: 100px;
  height: 100px;
  background-color: red;
  transition: left 1s ease;
  transform: scale(1);
  left: 0px;
}
<div id="main"></div>
<div id="test" class="test"></div>
Sheraff
  • 5,730
  • 3
  • 28
  • 53
  • requestAnimationFrame fires **before** the next paint, so it doesn't really help here if nothing triggered a reflow in between. You could wait for just a 0 timeout in the raf callback, but no need to wait anything, as explained in the linked dupe. – Kaiido Oct 25 '20 at 23:04
  • @Kaiido the link doesn't explain it IMO, since here the `.prepend` should already trigger a recalc (during current frame), and the RAF shouldn't execute on the current frame. What am I missing? – Sheraff Oct 26 '20 at 06:32
  • following the thread you linked, here's a full explanation that does use a single RAF for triggering an animation after removing a `display: none`https://stackoverflow.com/questions/54344996/css-transition-doesnt-work-if-element-start-hidden/54348963#54348963 – Sheraff Oct 26 '20 at 06:42
  • Yes it does explain it, across several links I admit. The `.prepend` should not trigger a reflow no, continue following the links until [this answer](https://stackoverflow.com/questions/47342730/javascript-are-dom-redraw-methods-synchronous/47343090#47343090) to better understand what is a reflow, and how it would be horrible to have every DOM manipulations like prepend to trigger one. Also, from the link you provide, I said (yes, I did write all these answers) you don't even need rAF, you can do it all synchronously as exposed in the marked-as dupe. https://jsfiddle.net/j9dotnyc/ – Kaiido Oct 26 '20 at 08:12