Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What purpose does void element.offsetWidth serve?

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>
like image 423
Leftium Avatar asked Mar 09 '26 23:03

Leftium


1 Answers

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:

  • You remove the class. The browser notes this, but doesn't redraw the screen yet
  • You add the class. The browser notes this, but doesn't redraw the screen yet
  • Your function finishes, and the browser decides to do the necessary calculations to make the page match what you've asked for... but nothing has changed! You've put the page back into the same state it had before the click handler began. Since nothing has changed, the display stays the same; no animation runs.

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);