为什么在Chrome中使用appendChild会触发重新计算样式,而设置innerHTML则不会?

3

使用Chrome开发者工具中的时间轴,我使用以下代码通过 innerHTMLappendChild 方法记录事件:

<!DOCTYPE html>

<html>
<head>
  <script>
    function testInnerHTML(){
      var wrap = document.getElementById('wrapper');
      wrap.innerHTML = "test";
    }
    function testAppendChild(){
      var wrap = document.getElementById('wrapper');
      wrap.appendChild(document.createTextNode("test"));
    }
  </script>
</head>

<body>
  <input type="button" value="innerHTML" onClick="testInnerHTML();"/>
  <input type="button" value="appendChild" onClick="testAppendChild();"/>
  <div id="wrapper"></div>
</body>
</html>

我发现:
- innerHTML触发了一个布局事件 innertHTML triggers layout event - appendChild触发了一个布局事件和一个重新计算样式事件 appendChild triggers layout and recalculate style events 在这个示例中,每个事件的处理时间几乎可以忽略不计,并且似乎是并行发生的,但是如果你在一个大型html表格中进行此操作,你会看到重新计算样式可能需要几秒钟,而布局将在此之后发生。所以如果我们能避免它,那就更好了!
我正在考虑使用一个带有文档片段的appendChild来避免innerHTML涉及到的解析。但是这种方式节省的时间却被重新计算样式所消耗...
我正在使用Chrome版本23.0.1271.64 m
你有什么想法为什么会出现这种情况吗? 我应该添加一些东西到我附加的节点上,以帮助浏览器不触发重新计算样式事件吗?
PS:如果你想知道双重解析事件的原因,请参考为什么在Chrome上点击设置innerHTML会触发两个解析事件?

"并且似乎更多或少是同时发生的" 这些事件标记具有最小的宽度,因此它们看起来重叠在一起。所有这些事件都发生在单个线程中。 - loislo
1个回答

2
在WebKit中,HTMLElement::setInnerHTML()在内部使用replaceChildrenWithFragment(),它具有以下特殊情况:
if (hasOneTextChild(containerNode.get()) && hasOneTextChild(fragment.get())) {
    toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data(), ec);
    return;
}

ContainerNode::appendChild()不同,该方法会迭代地将节点插入容器并分派相应的事件(例如DOMNodeInserted)。我认为这可能需要一些优化。

确实,它可以解释所给的简单示例。但是,您可以尝试更复杂的HTML字符串,仍然没有触发重新计算样式:wrap.innerHTML = <span>test</span>something more<table><tr><td>complicate</td></tr></table> - JBE
1
好的,您可以尝试创建一个相应的 DocumentFragment 并将其 appendChildwrap 中,以查看在这种情况下会发生什么。 - Alexander Pavlov

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