【问题标题】:Out-of-the-flow DOM Manipulation流外的 DOM 操作
【发布时间】:2014-06-07 06:19:20
【问题描述】:

https://developers.google.com/speed/articles/javascript-dom得到这个

据我了解,添加/删除元素会导致重排。换班也是如此。但在解决方案中,您要附加 删除,因此导致回流次数是问题代码的 2 倍。当然,并不是所有的回流都是平等的,那么类名更改回流比追加/删除回流更昂贵吗?我遗漏了什么使解决方案代码比问题代码更高效?

这种模式让我们可以创建多个元素并将它们插入到 DOM 触发单个回流。它使用一种叫做 文档片段。我们在 DOM 之外创建一个 DocumentFragment(所以 它是不合时宜的)。然后我们创建并添加多个元素 这。最后,我们将 DocumentFragment 中的所有元素移动到 DOM 但会触发一次回流。

问题

让我们创建一个函数来更改所有的 className 属性 元素内的锚点。我们可以通过简单的迭代来做到这一点 通过每个锚点并更新它们的 href 属性。问题 也就是说,这可能会导致每个锚点的回流。

function updateAllAnchors(element, anchorClass) {
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
}

解决办法

为了解决这个问题,我们可以从 DOM 中移除元素,更新 所有锚点,然后将元素插入到原来的位置。帮助 实现这一点,我们可以编写一个可重用的函数,它不仅删除 来自 DOM 的元素,但也返回将插入的函数 元素回到原来的位置。

/**
 * Remove an element and provide a function that inserts it into its original position
 * @param element {Element} The element to be temporarily removed
 * @return {Function} A function that inserts the element into its original position
 **/
function removeToInsertLater(element) {
  var parentNode = element.parentNode;
  var nextSibling = element.nextSibling;
  parentNode.removeChild(element);
  return function() {
    if (nextSibling) {
      parentNode.insertBefore(element, nextSibling);
    } else {
      parentNode.appendChild(element);
    }
  };
}

现在我们可以使用这个函数来更新元素内的锚点 那是不合流的,只有当我们移除 元素以及当我们插入元素时。

function updateAllAnchors(element, anchorClass) {
  var insertFunction = removeToInsertLater(element);
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
  insertFunction();
}

【问题讨论】:

    标签: javascript html css dom


    【解决方案1】:

    假设您要更改包含 100 万个元素的类。

    直接执行会导致 100 万次重排 - 每个类一个 -。

    但是如果你从 DOM 中移除它的父类,更改所有类,然后将其重新插入,那只有 2 次重排——因为更改文档外的元素不会导致重排——。

    所以基本上,如果你有很多元素,删除和重新插入会更有效。如果您只有几个,则无需这样做。

    【讨论】:

      【解决方案2】:

      因此,文档片段存在于“内存中”,而不是页面上。操作不会触发任何重绘/流,因为片段在任何地方都没有可视化表示。当你把它放在页面上时,一旦你完成了对它的操作,浏览器就会知道它的结构、类、内容等,所以只需要重排/绘制一次。

      在第一个示例中,当您遍历锚点并更改类名称(可能也更改其样式)时,它将立即应用该类,找到新样式并重新绘制该链接。然后对下一个做同样的事情。这很慢。

      通过将其全部拉入内存并在那里操作 DOM,当您将父包装元素重新插入页面时,您只有一个重绘/流程。

      【讨论】:

        【解决方案3】:

        根据解决办法:

        为了解决这个问题,我们可以从 DOM 中移除元素,更新 所有锚点,然后将元素插入到原来的位置。

        因此,在这种情况下,它将触发 2 次回流(1 次用于移除,1 次用于插入)。因此,当您想要一次修改 2 个以上的元素时,此解决方案适用。

        【讨论】:

          猜你喜欢
          • 2015-06-14
          • 2017-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-01
          • 2022-11-23
          • 2011-02-19
          相关资源
          最近更新 更多