The other solutions are good, but I wanted to provide an alternative and explain what is happening.
The reason it's not working as expected in your example is because all of the CSS properties are changed and then a browser reflow event is triggered and the transition starts. In other words, the transition
property is set to none
, then the left
property is changed, then the transition
property is changed back to left 1s
.
After all the style properties have been updated, a browser reflow event is triggered, the CSS is repainted, and then the transition starts:
element.style.transition = "none";
element.style.left = px + "px";
element.style.transition = "left 1s";
// The transition starts after all the CSS has been modified and a reflow has been trigged.
There are a few reasons it is executed like this.
The main reason is performance. Rather than repainting each element after changing a single CSS property, it is much more efficient to change all of the properties and have a single repaint event. Additionally, if you are transitioning multiple properties you would expect each property to be changed before the element is transitioned (which is exactly what is happening).
If you want a clean alternative that doesn't involve any timeouts/delays or forced reflows, you could simply set the transitionDuration
property to 0s
before setting the value, and then remove this inline style when transitioning the element.
For example:
var element = document.getElementById("theElement");
function setX(px) {
element.style.transitionDuration = '0s';
element.style.left = px + "px";
}
function animateX(px) {
element.style.transitionDuration = '';
element.style.left = px + "px";
}
#theElement {
position: relative;
width: 100px;
height: 100px;
border: 2px solid;
left: 200px;
transition: left 1s;
}
<div id="theElement"></div>
<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>
Similarly, you could also toggle a class on the element before transitioning or setting the value:
var element = document.getElementById("theElement");
function setX(px) {
element.classList.remove('transition');
element.style.left = px + "px";
}
function animateX(px) {
element.classList.add('transition');
element.style.left = px + "px";
}
#theElement {
position: relative;
width: 100px;
height: 100px;
border: 2px solid;
left: 200px;
}
#theElement.transition {
transition: left 1s;
}
<div id="theElement"></div>
<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>
The above snippet works, but it's also worth pointing out that you can listen to the transitionend
event and remove the class when the transition ends:
For instance:
function animateX(px) {
element.classList.add('transition');
element.style.left = px + "px";
element.addEventListener('transitionend', callback);
function callback() {
this.classList.remove('transition');
this.removeEventListener('transitionend', callback);
}
}