为什么删除元素比添加元素需要更多的时间?

21
我有一个拥有很多行的表格(大约10,000行)。 (注:我知道拥有这么大的表格没有意义,但我想了解下面的行为)。 当我收到新数据时,我想先清除行。奇怪的是,清除表格比从头开始构建行要花费更长的时间。使用html("")或纯JS innerHTML = ""清除表格行需要1.5分钟,远远多于构建行本身所需的不到一秒钟。
因此我的问题是: - 为什么删除元素比添加元素需要更多的时间?发生了什么“幕后”? (请注意,我的问题是一个“为什么”的问题,我不寻找可能的解决方法)。
更新: 我注意到表格行和单元格有浮动定义,当我去掉浮动时,表格会立即清空。 我仍然非常想知道为什么浮动会使行的删除变得如此缓慢。 这里是一个fiddle: https://jsfiddle.net/maaikeb/t5pduuue/ 在Chrome中,清空表格需要25秒,而将其附加到DOM中只需要23毫秒。
*我阅读了以下帖子,但它们更多地涉及可能的解决方法,而不是为什么删除元素比添加元素需要更多的时间,当您删除它们时实际发生了什么。

jQuery empty非常慢
jQuery empty非常慢
使用jQuery删除表格行的最佳方法是什么?
在JavaScript中删除表格行
jquery-从非常大的表中删除所有行的最快方法}


1
如果您要替换所有行,为什么不直接删除整个表?我认为这是一个好问题,需要回答,但是针对您现在的问题,也许您应该考虑这一点 :) - Dejan.S
2
@GaneshRadhakrishnan 请看下面我的评论 - 显然展示那么多行的表格是没有意义的,我会添加分页。但我仍然想了解为什么删除元素比添加元素更耗费性能。 - Kemeia
1
我相信这回答了你的一些问题。 - Samuil Petrov
2
在哪个浏览器中? - Ry-
2
你能在这个 JSFiddle 上重现吗?https://jsfiddle.net/yqc92zqd/ - Kaiido
显示剩余9条评论
5个回答

2
我假设您在IE上的性能不佳。我认为这是因为$.empty方法在表中删除每个元素时都会在循环中调用removeChild方法,而该方法在IE上始终性能不佳 - 请参见此文章进行短期案例研究,其中包括一些性能比较和解决方案。
无论您如何使用大型DOM,在IE上都会有性能问题。这篇关于IE上大型DOM中appendChild的文章就是一个例子,说明社区仍然被这个简单的事实所困扰。 :)

实际上,这不仅发生在IE上,而且也发生在Chromium和Firefox ESR上。 - Kemeia
请问您能否提供两者的确切版本?因为它们过去都曾受到与“removeChild”和“addChild”相关的相同问题的困扰。 - sztrzask
Firefox ESR 45.9.0 和 Chromium 57.0.2987.98 - Kemeia
你能创建一个fiddle或codepen吗?以前在任何浏览器上都有相当多的错误,例如Chrome的removeChild在css-heavy页面上非常缓慢。我无法真正重现这个问题 - 在我的Chrome上大约需要200毫秒。 - sztrzask
你说得有道理 - 我试图创建一个fiddle来重现这种情况,但是它并没有重现出来。 在我的代码中进一步挖掘后,我发现如果我删除一个使所有td元素浮动的类,问题也会得到解决。 我试图再现后者,但也没有成功 - 我需要进一步挖掘。 - Kemeia
显示剩余2条评论

1
如果你问为什么清空表格需要比添加表格慢1000倍,那么,实际上不应该是这样的:这是一个错误。
你会发现浏览器、版本、jQuery版本或操作系统都可能存在重要的性能差异,但25秒与23毫秒之间的差距太大了。似乎你在某个地方进行了O(N^2)操作。对于小于此规模的问题,人们会提交票据并得到浏览器开发者的认可。
无论如何,删除元素并不总是比添加元素快。我们的直觉告诉我们“销毁”应该比“创建”更快,但在操作DOM时并不一定如此。jQuery和浏览器都需要额外的工作来移除元素。
如果您查看当前(3.2.1)jQuery append() function的实现,您会发现它基本上使用本地JS方法appendChild()。另一方面,jQuery empty() functionhtml()使用empty())需要循环遍历所有内部元素,并注意删除相关数据(例如事件)以防止内存泄漏。
而且,如果您仅使用原始JS,浏览器仍然需要检查大量内容以删除元素。如果您想进入浏览器的内部,可以查看此Firefox issue
无论您选择使用 removeChild()innerHtml 还是 textContent(),浏览器总是需要重新计算样式和布局:请参见Chromium中的这个
这种错误很常见。如果你在Chromium中搜索关键词“removeChild”和“slow”,你会得到一个相当长长的列表。有时它们会出现并消失,因为修复引入了回归,就像这个案例涉及浮动元素一样。这个案例对你来说可能特别有趣,因为它证明了一些错误与你选择移除元素的方法无关,并且,像你的情况一样,它出现在将复杂的CSS规则应用于那些元素时(表明浏览器触发了回流)。
我无法在Firefox和Chrome中重现您的问题,因此无法确定25秒或1.5分钟的确切原因,但似乎是在更近期的版本中修复的错误。如果您仍然使用旧版本并且能够重现该问题,则可以值得检查是否按相反顺序(从lastChild开始)删除元素有所帮助。也许您可以避免在删除后所有浮动同级元素的位置重新计算。

下次您可能希望打开一个票证并跟踪他们如何找到错误并解决它。这将满足您的好奇心,您可以学习很多关于浏览器内部的知识!


谢谢你的详细回答!你说得对,这是一个Chromium特有的bug。我正在使用57.0.2987.98版本的Chromium,在Firefox上没有这种情况发生。你提供的链接中的关于在具有大量CSS的元素上使用removeChild所引起的问题似乎与这个问题非常相似,只不过这个问题似乎已经被修复了。 - Kemeia
@Kemeia。我特意在问题跟踪器中寻找了类似于你的问题在版本57和61之间(我的版本运行良好),但是我没有找到。也许它已经作为其他问题的一部分得到修复,也许从未被报告过。如果你升级到61或更高版本仍然遇到问题,那么问题可能出在其他地方。如果发生这种情况,请告诉我,我们可以进一步调查! - David

0
你只是想知道为什么。但也许其他人想知道加快速度的方法...因此,我想指出我遇到了什么。
我曾经有一个问题,无法快速删除我之前动态创建的元素(这是SVG动画的一部分...那种类型的)。
我的解决方案是将之前创建的元素存储在数组中。稍后进行删除时,只需通过数组迭代即可将它们删除。这样做更快,并且对我有用。
最终,对于我的情况,它不是.remove()(或.removeChild())函数本身的性能问题,而是DOM扫描指定元素(如David在回答中提到的)导致了进程放缓。

0

不必循环整个DOM并进行删除和重新渲染,您可以使用hide()和show()。这样做快速且对性能有好处,因为检索隐藏列也非常容易。请发现这个片段很有用。

//did based on checkbox here update to ur requirement hide() callback remains same.

$("input:checkbox:not(:checked)").each(function() {
    var column = "table ." + $(this).attr("name");
    $(column).hide();
});

$("input:checkbox").click(function(){
    var column = "table ." + $(this).attr("name");
    $(column).toggle();
});
td, th {
    padding: 6px;
    border: 1px solid black;
}

th {
    background-color: #f0eae2;
    font-weight: bold;
}

table {
    border: 1px solid black;
}
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<p><input type="checkbox" name="first_name" checked="checked"> First Name 
  <input type="checkbox" name="last_name"> Last Name 
  <input type="checkbox" name="email" checked="checked"> Email</p>

<table id="report">
<thead>
<tr>
 <th class="first_name">First Name</th>
 <th class="last_name">Last Name</th>
 <th class="email">Email</th>
</tr>
</thead>
<tbody>
<tr>
 <td class="first_name">Larry</td>
 <td class="last_name">Hughes</td>
 <td class="email">larry@gmail.com</td>
</tr>
<tr>
 <td class="first_name">Mike</td>
 <td class="last_name">Tyson</td>
 <td class="email">mike@gmail.com</td>
</tr>
</tbody>


0

您可以通过将表格的内部HTML赋值为空字符串来清除它。然后它就不会遍历每个行元素以查找标题、tbody或行来删除它。因此,这比您当前的方法更快。

$("#yourtableid").html("");


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