void element.offsetWidth; is a curious line of code that would seem to do nothing, but is required for a CSS animation to work. What does this line do and why is it needed?
If the entire line is commented out, the animation happens once but does not repeat. (It still works if the void is removed.)
The full code/demo is live here; I tried to copy the relevant excerpts below:
script.js:
beatIndicator = document.getElementById("beatIndicator"),
...
// Happens every time a beat starts:
beatIndicator.classList.remove("anim");
void beatIndicator.offsetWidth; // Why is this line needed?
beatIndicator.classList.add("anim");
example-advanced.html
<span style="display: none;" id="beatIndicator" class="pulse"></span>
<style>
.pulse {
width: 40px;
height: 40px;
border-radius: 50%;
background: #f44336;
z-index: 3;
left: -18px;
display: inline-block;
vertical-align: middle;
margin-left: 10px;
}
@keyframes pulse-anim {
from {
box-shadow: rgba(244, 67, 54, 1) 0px 0px 0px 0px;
}
to {
box-shadow: rgba(244, 67, 54, 0) 0px 0px 0px 14px;
}
}
.pulse.anim {
animation: pulse-anim;
}
</style>
When you make changes to the dom, such as classList.remove('anim'), this can result in a cascade of changes to the page. Rendering those changes can be expensive, and since it's pretty common that you'll change multiple things in a row, browsers try to batch up accumulated changes and only perform the calculation at the end of what you're doing.
So if that weird line wasn't there, then here's what would happen:
What that extra line of code is doing is asking the browser to give you information about the dom. But in order to know what the offsetWidth is, the browser has to abandon its plan of batching the changes and perform the reflow of the page right now. The current state has no run-animation class, which is a change, and so the animation gets reset. And later when the function finishes, it performs the calculations again and sees that you've made yet another change, relative to when it provided you with the offsetWidth, so it applies that too.
In short: You're forcing the browser to do extra work, and it just so happens that in this case that is desirable. In most cases, forcing the browser to do extra work is a bad thing. Most of the time you want to avoid querying the dom in this way, since it can reduce performance.
Another way to get this to work would be to remove the class now, and then add the class a short time later, as in:
element.addEventListener("click", function(e){
element.classList.remove("anim");
setTimeout(() => element.classList.add("anim"), 0);
}, false);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With