在一次操作中更改元素属性(数据集) - 避免回流

Posted

技术标签:

【中文标题】在一次操作中更改元素属性(数据集) - 避免回流【英文标题】:Change element attributes (datasets) in one operation - avoid Reflows 【发布时间】:2021-11-22 17:06:57 【问题描述】:

我试图避免CSS Reflows, 通常DocumentFragment 足以满足我的需求。

当我修改/添加/删除datasets时,我有一些案例, 问题是每个dataset 都需要一个导致回流的操作..

element.dataset 是一个只读对象,所以我想知道如何在这个例子中只用一个回流而不是 3 个来做到这一点?

element.dataset.a='1'
delete element.b
element.dataset.c='2'

完全替换元素是实现此目的的唯一方法吗?

【问题讨论】:

在观看了here 关于 CSS 回流的视频后,我不确定他们甚至不知道他们在说什么。根据我的编程逻辑,我很确定 DOM 是循环的,所以任何更具体的内容实际上都会阻止绘制 DOM 树。太多的动画或变换可能是一个问题。 DocumentFragment 仅用于将内容附加到document。我不建议使用它,因为在将事件附加到 DOM 之前,您不能将事件附加到 DocumentFragment 中的节点。 【参考方案1】:

更改元素的数据集不会导致同步回流。您可以在同一任务中多次执行此操作。

要测试某些东西是否会导致回流,您可以使用 CSS 过渡。 从已知状态开始,然后设置中间状态,触发应该导致回流的原因,最后恢复原始状态。 如果测试的操作确实触发了重排,那么将发生从中间状态到最终状态的新转换。如果没有发生回流,则不会发生转换。

function testReflow(func) 
  return new Promise( (res, rej) => 
    const elem = document.querySelector(".reflow-tester");
    // set "intermediary" values
    elem.style.opacity = 1;
    elem.style.transition = "none";
    try  func(elem);  catch(err)  rej(err) 
    elem.style.opacity = 0;
    elem.style.transition = "opacity 0.01s";

    // if the tested func does trigger a reflow
    // the transition will start from 1 to 0
    // otherwise it won't happen (from 0 to 0)    
    elem.addEventListener("transitionstart", (evt) => 
      res(true); // let the caller know the result
    ,  once: true );
    // if the transition didn't start in 100ms, it didn't cause a reflow
    setTimeout(() => res(false), 100);
  );


(async () => 
  // wait 1s before executing the tests to be sure we're not in weird first paint
  await new Promise((res) => setTimeout(res, 1000));
  // first testing with a well known reflow trigger
  const offsetWidth = await
  testReflow((elem)=>elem.offsetWidth);
  console.log("offsetWidth getter:", offsetWidth);
  // now with dataset
  const dataset = await testReflow((elem) => 
    elem.dataset.foo = "bar";
    elem.dataset.bar = "baz";
    elem.dataset.baz = "bla";
  );
  console.log("dataset:", dataset);
)().catch(console.error);
.reflow-tester 
  opacity: 0;
<div class="reflow-tester">Tester</div>

【讨论】:

以上是关于在一次操作中更改元素属性(数据集) - 避免回流的主要内容,如果未能解决你的问题,请参考以下文章

重绘和回流的解释

回流与重绘

前端回流与重绘之简介

重绘回流(重排)

重绘回流(重排)

回流/重绘问题?优化太慢的应用