为啥删除元素比添加元素需要更多时间?
Posted
技术标签:
【中文标题】为啥删除元素比添加元素需要更多时间?【英文标题】:Why does removing elements take more time than adding elements?为什么删除元素比添加元素需要更多时间? 【发布时间】:2018-02-19 08:30:20 【问题描述】:我有一个包含很多行的表(例如 10.000)。
(注意:我意识到拥有这么大的表没有意义,但我想了解以下行为)。
当我收到新数据时,我想先清除行。奇怪的是,清理表比从头开始构建行需要更长的时间。使用 html("")
或普通 JS innerHTML = ""
清除表行需要 1.5 分钟,这比构建行本身花费的时间不到一秒要多得多。
所以我的问题: - 为什么删除元素比添加元素需要更多时间? “幕后”会发生什么? (请注意,我的问题是为什么,我不是在寻找可能的解决方法)。
更新:
我注意到表格行和单元格应用了浮动定义,当我删除浮动时,表格在一个实例中被清空。 我仍然很想了解为什么浮点数会使删除行的速度慢得多。 这里有一个小提琴:https://jsfiddle.net/maaikeb/t5pduuue/ 在 Chrome 中,清空表格需要 25 秒,而将其附加到 DOM 只需 23 ms。
*我阅读了下面的帖子,但他们更多地讨论了可能的解决方案,而不是为什么删除元素比添加它们需要更多时间,当你删除它们时实际会发生什么
jQuery empty is very slowjQuery empty is very slowWhat is the best way to remove a table row with jQuery?Deleting table rows in javascriptjquery - fastest way to remove all rows from a very large table
【问题讨论】:
Fastest way to add/remove multiple elements in DOM的可能重复 如果要替换所有行,为什么不直接删除整个表?我认为这个问题很好,需要一个答案,但我的意思是你的问题现在你可能应该考虑它:) @GaneshRadhakrishnan 请在下面查看我的评论 - 显然显示有这么多行的表格没有意义,我将添加分页。我仍然想了解为什么删除元素比添加元素更需要性能 在哪个浏览器中? 你能重现这个小提琴吗? jsfiddle.net/yqc92zqd 【参考方案1】:我假设你在 IE 上表现不佳。
我会说这是因为$.empty
方法在循环中为表中的每个已删除元素调用removeChild
,并且该方法在 IE 中的性能总是很差 - 请参阅this article for short case study 以及一些性能比较和解决方案。
无论您如何处理大型 DOM,您都会在 IE 上表现不佳。 This article on appendChild in large DOM on IE 是社区仍然被这个简单事实所迷惑的一个例子。 :)
【讨论】:
其实这不是在 IE 中,而是在 Chromium 和 Firefox ESR 上都发生过 能否请您提供两者的确切版本?因为他们过去曾被removeChild
和 addChild
的相同问题所困扰。
Firefox ESR 45.9.0 和 Chromium 57.0.2987.98 谢谢
你能创建一个小提琴或codepen吗?曾经在这两种浏览器上都有相当多的错误,例如Chrome 的 removeChild
是 very slow on css-heavy page. 我无法真正重现 - 在 Chrome 上对我来说大约是 200 毫秒。
你说得有道理 - 我试图创建一个重现场景的小提琴,但它不会重现。在我的代码中进一步挖掘,我发现如果我删除一个使所有 td 元素浮动的类,问题也得到了解决。我试图重现后者,但也没有成功 - 我需要进一步挖掘【参考方案2】:
您可以使用 hide() 和 show(),而不是循环整个 DOM 删除并再次重新渲染。由于检索隐藏列也非常容易,因此速度快且性能好。请发现该片段很有用。
//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>
【讨论】:
【参考方案3】:您可以通过为其内部 HTML 分配空字符串的值来清除表格。然后它不会遍历每个行元素来查找标题、tbody 或行来删除它。因此会比您当前的方法更快.
$("#yourtableid").html("");
【讨论】:
【参考方案4】:如果你问为什么清空表格比追加表格要花费 1000 倍的时间,好吧,它不应该:这是一个错误。
你可以发现重要的性能差异取决于浏览器、它的版本、jQuery 或操作系统的版本,但 25 秒和 23 毫秒实在是太多了。似乎比您在某个地方遇到了O(N^2)
操作。低于这个数字的人打开票证并且浏览器的开发者承认他们。
无论如何,删除元素并不总是比添加元素更快。我们的直觉告诉我们 destroy 应该比 create 快,但在操作 DOM 时,这不一定是正确的。 jQuery 和浏览器都需要做额外的工作才能取出元素。
如果您查看jQuery append()
function 的当前(3.2.1)实现,您会发现它的基本作用是使用本机JS 方法appendChild()
。另一方面,jQuery empty()
function(html()
使用 empty()
)需要遍历所有内部元素,并注意删除每个内部元素的相关数据(例如事件)以防止内存泄漏。
而且,如果你只使用 vanilla JS,浏览器在删除元素时仍然需要检查一大堆东西。如果你想了解浏览器的内部结构,可以查看Firefox issue。
不管你选择removeChild()
、innerHtml
还是textContent()
,浏览器总是需要重新计算样式和布局:检查this one in Chromium。
这种错误很常见。如果你在 Chromium 中搜索关键字“removeChild”和“slow”的问题,你会得到一个漂亮的long list。有时它们来来去去是由于引入回归的修复,比如在 this case 中涉及浮动元素。 This other one 对您来说可能特别有趣,因为它证明某些错误与您选择删除元素的方法无关,并且因为就像您的情况一样,它会在将复杂的 CSS 规则应用于该元素时出现(建议比浏览器触发重排)。
我无法在 Firefox 和 Chrome 中重现您的问题,因此我无法解决 25 秒或 1.5 分钟的确切原因,但这似乎是最近版本中修复的错误。如果您仍然拥有相同的旧版本并且可以复制它,则可能值得检查以相反的顺序(从lastChild
开始)删除元素是否有帮助。或许您避免在移除后重新计算所有浮动兄弟姐妹的位置。
下一次,您可能想开一张票并按照他们如何找到错误并修复它。它会满足你的好奇心,你可以学到很多关于浏览器内部的知识!
【讨论】:
感谢您的详尽回答!你是对的,这是一个 Chromium 特定的错误。我正在使用 Chromium 57.0.2987.98,在 Firefox 上它不会发生。您在具有大量 CSS 的元素上与 removeChild 链接的错误似乎引起了非常相似的问题,只是它似乎已被修复。 @Kemeia。我专门在问题跟踪器中查找版本 57 和 61 之间的错误(我的,它工作顺利),但我找不到它。也许它作为其他问题的一部分得到了修复,也许它从未被报告过。如果您更新到 61 或更高版本但仍有问题,则问题可能出在其他地方。如果发生这种情况,请告诉我,我们可以进一步研究!【参考方案5】:你只是想知道为什么。但也许其他人想知道一种加快速度的方法......所以我只想指出我遇到了什么。
我在删除之前动态创建的元素时遇到了问题(是 svg 动画的一部分……有点)。 我的解决方案是将之前创建的元素存储在一个数组中。后来为了删除,我只需要遍历数组来删除它们。那要快得多,并且对我有用。 最后,就我而言,这不是 .remove()(或 .removeChild())函数本身的性能,而是对指定元素的 DOM 扫描(如 Davids 回答中所述)减慢了进程。
【讨论】:
以上是关于为啥删除元素比添加元素需要更多时间?的主要内容,如果未能解决你的问题,请参考以下文章
List.of() — 为啥要提供一个无法添加/删除/编辑的空元素列表? [复制]