0

As you can see in the code below. If you change the style and then change it again right away, there is no transition. However, if you just wait a little bit, it will work. I'm wondering if there's anything that can be monitored to know when you can change the style again such that it won't bypass the transition.

let one = document.getElementById('one');
let two = document.getElementById('two');
one.style.left = '100px';
one.style.left = 0;
two.style.left = '100px'
setTimeout(()=> two.style.left = 0, 100);
div {
  position: absolute;
  width: 100px;
  height: 100px;
  border: solid;
  transition: left 1s;
}

#two {
  top: 150px;
}
<div id='one'></div>
<div id='two'></div>

Edit: This appears to be a simple case of using an no-delay setTimeout, and indeed that does work for my example code. However, in my actual code that does not work. I'm not sure what's going on. I am unable to make a simple example of it that doesn't work but I'm guessing it has something to do with having more content on the page.

Mason
  • 738
  • 7
  • 18
  • whats your doubt Not understanding please explain clearly – supriya suresh g Jun 14 '19 at 06:47
  • 1
    No, there isn’t anything to “monitor”, this is due to how responsibilities are shared between the JS runtime and the rendering engine. As long as some “closed” JS functionality is running, the rendering engine sits dormant, and only gets handed control again, when the JS part is finished. The usual solutions to this are either setTimeout/requestAnimationFrame to force a “break” in the JS execution, or to read some value that needs querying the rendering engine to determine, such as the offsetHeight of an element. – 04FS Jun 14 '19 at 06:53
  • @04FS For my actual issue, `setTimeout` does work if I make the delay big enough, but not if I make it have no delay. I had never heard of `requestAnimationFrame`. I just tried it however, and that also didn't work. But yes, perhaps I can monitor `getBoundingClientRect()` with `setInterval` to see when its `x` value changes. I will give that a try tomorrow if no better solution is posted. – Mason Jun 14 '19 at 08:07

2 Answers2

0

You are approaching your problem the wrong way.

In fact, if you change your setTimeout duration to 0 seconds, your transition will work.

let one = document.getElementById('one');
let two = document.getElementById('two');
one.style.left = '100px';
one.style.left = 0;
two.style.left = '100px'

// Change timeout duration to 0
setTimeout(()=> two.style.left = 0, 0);
div {
  position: absolute;
  width: 100px;
  height: 100px;
  border: solid;
  transition: left 1s;
}

#two {
  top: 150px;
}
<div id='one'></div>
<div id='two'></div>

But why? If the duration is 0, Isn't it supposed to mean it will run immediately?

Yes, but no.

When you assign immediately:

two.style.left = '100px'
two.style.left = 0

Things get processed so fast that by the time the rendering engine gets the data, two is already at left: 0. So no transition will take place.

When you use setTimeout, it basically queues the task to the end of current queue. So it gives time for the rendering to register left = '100px', then left = 0.

In essence, you cannot and don't have to monitor when you can change style. All you need to do is queue the job to the "next tick".

The above explanation is overly simplified. See this question for more information. See this question for some demonstration and also some detail explanation.

yqlim
  • 6,898
  • 3
  • 19
  • 43
  • Doesn't really explain why it works and why it doesn't work without the setTimeout – charlietfl Jun 14 '19 at 07:02
  • @charlietfl was writing a more detailed answer. See updated. – yqlim Jun 14 '19 at 07:06
  • Very interesting, I actually didn't even try doing `setTimeout` with no delay in this example, because (I thought) this was a simplified version of the issues I was having. Unfortunately this is not the answer however, because in the actual code I'm using, `setTimeout` with no delay doesn't work (occasionally it will, but most of the time it doesn't). – Mason Jun 14 '19 at 08:00
  • @Mason in that case, can you share a more detailed code? From your current code snippet, that's the only problem I'm seeing. – yqlim Jun 14 '19 at 08:01
  • @YongQuan I really need to go to sleep right now. I will get on that tomorrow. – Mason Jun 14 '19 at 08:16
0

This does the job both in my example code and my real code.

let one = document.getElementById('one');
let two = document.getElementById('two');
one.style.left = '100px';
one.style.left = 0;
let left = two.getBoundingClientRect().left;
two.style.left = '100px'
let i = setInterval(()=>{
  if (two.getBoundingClientRect().left != left) {
    two.style.left = 0;
    clearInterval(i);
  }
});
div {
  position: absolute;
  width: 100px;
  height: 100px;
  border: solid;
  transition: left 1s;
}

#two {
  top: 150px;
}
<div id='one'></div>
<div id='two'></div>
Mason
  • 738
  • 7
  • 18