流出DOM操作

时间:2014-04-22 18:00:37

标签: javascript html css dom

来自https://developers.google.com/speed/articles/javascript-dom

根据我的理解,追加/删除元素会导致重排。和改变班级一样。但是在解决方案中,您将附加删除,从而导致两次次重排作为问题代码。当然,并非所有的回流都是相同的,所以类名改变回流比追加/去除回流更昂贵吗?我错过了什么使得解决方案代码比问题代码更有效?

  

这种模式让我们可以创建多个元素并将它们插入到   DOM触发单个回流。它使用一种叫做a的东西   DocumentFragment的。我们在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();
}

3 个答案:

答案 0 :(得分:2)

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

直接这样做会导致100万次回流 - 每个班级 - 。

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

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

答案 1 :(得分:1)

因此,文档片段存在于内存中,而不是在页面上。操作不会触发任何重新绘制/流动,因为片段在任何地方都无法直观显示。当你把它放在页面上时,一旦你完成操作它,浏览器就会知道它的结构,类,内容等,所以只需要重排/绘制一次。

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

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

答案 2 :(得分:1)

根据解决方案:

  

要解决这个问题,我们可以从DOM中删除元素,进行更新   所有锚点,然后将元素插回到原来的位置。

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