void element.offsetWidth有什么作用?

10

void element.offsetWidth;是一行看似无用的代码,但在CSS动画中是必需的。这行代码是做什么的,为什么需要它呢?

如果将整行注释掉,动画会执行一次但不会重复。(如果删除void,它仍然有效)。

完整的代码/演示在此处可用;我试着复制下面的相关摘录:

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>
1个回答

15
当您对DOM进行更改时,例如classList.remove('anim'),这可能会导致页面上一系列的变化。渲染这些变化可能是昂贵的,而且由于在一连串操作中通常会更改多个内容,因此浏览器尝试将累积的更改批量处理,并且仅在您完成操作后才执行计算。
所以,如果没有那条奇怪的代码行,将会发生以下情况:
  • 您删除了类名。浏览器记录了这一点,但尚未重新绘制屏幕
  • 您添加了类名。浏览器记录了这一点,但尚未重新绘制屏幕
  • 您的函数执行完毕,浏览器决定进行必要的计算以使页面与您所要求的匹配... 但是,什么都没有改变!您已经将页面恢复到了单击处理程序开始之前的状态。由于没有发生任何变化,显示保持不变;动画也不会运行。
那段额外的代码会要求浏览器提供关于DOM的信息。但是为了知道offsetWidth是什么,浏览器必须放弃对更改的批处理计划并立即执行页面的回流操作。当前状态没有运行动画类,这是一个更改,所以动画被重置。稍后当函数完成时,它会再次执行计算,发现你又进行了一次更改,相对于它提供了offsetWidth时,所以也应用了这个更改。
简而言之:你迫使浏览器做额外的工作,而在这种情况下这是可取的。大多数情况下,强制浏览器做额外的工作是不好的。大多数情况下,你想避免以这种方式查询DOM,因为它可能会降低性能。
使其正常工作的另一种方法是立即移除该类,然后稍后再添加该类,例如:
element.addEventListener("click", function(e){
  element.classList.remove("anim");
  setTimeout(() => element.classList.add("anim"), 0);
}, false);

非常棒的解释。我通过返回元素的实际offsetWidth来测试您的答案,它与使用void返回undefined一样有效。仅进行测量就足以确保动画重置到起点。 - myNewAccount

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接