Manngo Manngo - 2 months ago 14
Javascript Question

JavaScript: Trigger CSS Transition with window.setTimeout

This is (obviously) part of a bigger project, but I am trying to trigger a CS transition on

setTimeout
.

(I know about using CSS animations, but this is not just about repeated transitions).

A CSS transition will occur when a property changes. For my own purposes, I use
setAttribute
as that isolates the behaviour from other class-related stuff, but the behaviour below works the same.

Assuming the HTML and other code is in place, here is simplified version:

var test=document.querySelector('div#test');
window.setInterval(doit,4000);
function doit() {
test.removeAttribute('thing');
test.innerHTML=new Date();
test.setAttribute('thing',null);
}


The CSS is:

div#test {
opacity: 0;
}
div#test[thing] {
transition: opacity 1s;
opacity: 1;
}


The trick I am trying is to change clear the attribute and then set it again — that should trigger the CSS change. It doesn’t work as above. However, if I delay setting the attribut by 0 seconds, it does work:

function doit() {
test.removeAttribute('thing');
window.setTimeout(function() {
test.setAttribute('thing',null);
},0);
test.innerHTML=new Date();
}


The question is: why does it not work unless I add this minuscule delay, and is there a better way to do this?

I would like an answer without jQuery.

Note: I have accepted an answer below. The following code will work:

function doit() {
test.removeAttribute('thing');
test.offsetHeight; // force update
test.innerHTML=new Date();
test.setAttribute('thing',null);
}


Thanks

Answer

This is because of how the browser queues reflow and relayout changes. The browser does not do these things sequentially like how you've structured your code. It adds the changes to a queue and attempts to perform multiple operations at once (for performance reasons).

The browser is trying to be "smart" here - it sees that the styles are going to wind up exactly as they were before, so the removal and re-addition of the attribute are batched together in one update and thus you see no change. Adding the setTimeout, even with a 0 second delay, forces the browser to reflow at that point.

Here's a link to another similar question: Force browser to trigger reflow while changing CSS

And here's a comprehensive list of methods/properties you can call in JS to force a reflow in your JS (instead of using setTimeout): https://gist.github.com/paulirish/5d52fb081b3570c81e3a