20

I have a table with a lot of rows (like 10.000).
(Note: I realize it doesn't make sense to have a table that large, but I'd like to understand the below behavior).
When I receive new data I want to clear the rows first. Weirdly enough, clearing the table takes much longer than building the rows from scratch. Clearing the table rows with html("") or plain JS innerHTML = "" takes 1.5 minutes, much more than building the rows themselves, which takes less than a second.

So my question:
- Why does removing elements take more time than adding them? What happens 'behind the scenes'?
(Please note my question is a Why one, I'm not looking for possible workarounds).

UPDATE:

I noticed that the table row and cells have floating definitions applied to them, and when I remove the float, the table is emptied out in an instance.
I'd still really like to understand why the float makes the removal of the rows so much slower.
Here a fiddle: https://jsfiddle.net/maaikeb/t5pduuue/
In Chrome it takes 25 seconds to empty the table, and only 23 ms to append it to the DOM.

*I read the below posts, but they talk more about possible solutions, and less about why removing elements takes more time than adding them, what actually happens when you remove them

jQuery empty is very slow
jQuery empty is very slow
What is the best way to remove a table row with jQuery?
Deleting table rows in javascript
jquery - fastest way to remove all rows from a very large table

Kemeia
  • 696
  • 1
  • 7
  • 19
  • 1
    Possible duplicate of [Fastest way to add/remove multiple elements in DOM](https://stackoverflow.com/questions/7307087/fastest-way-to-add-remove-multiple-elements-in-dom) – xxxmatko Sep 11 '17 at 07:10
  • 1
    If you are replacing all of the rows why not just remove the whole table? I think the question is good though and needs an answer but I mean for your issue now you maybe should consider it :) – Dejan.S Sep 11 '17 at 07:13
  • 20 seconds for 10.000 rows isnt worth? I think yes – SilverSurfer Sep 11 '17 at 07:13
  • Tables with 10,000 rows is a headache. Why can't u use pagination? – Ganesh Radhakrishnan Sep 11 '17 at 07:22
  • 1
    @xxxmatko this post doesn't really answer the Why question, it is also not part of the question – Kemeia Sep 11 '17 at 07:23
  • 2
    @GaneshRadhakrishnan please see my comment below - obviously it doesnt make sense to show a table with that many rows, and I'll add pagination. Still I'd like to understand why removing elements is so much more performance intensive than adding them – Kemeia Sep 11 '17 at 07:25
  • @Dejan.S `$('#my-table').remove()` takes slightly less time, 1:18 seconds – Kemeia Sep 11 '17 at 07:26
  • @marmai How much time it takes add these rows first time and how do you receive new data? via ajax? can you please share your code and how are html tags build? – SilverSurfer Sep 11 '17 at 07:28
  • @SilverSurfer adding the rows takes less than a second. I'm basically creating an HTML string in a loop and then do append all at once. I can share code a bit later (I'm in the middle of something), though I'm not sure how much that is relevant. – Kemeia Sep 11 '17 at 07:32
  • @marmai Can be relevant, removes takes much more time than adding first time all at once, so you can have the query code with HTML in a file, and each time you need update the table you can replace entire code with new table via AJAX, so you wouldnt need remove() or similar, you would increase performance. – SilverSurfer Sep 11 '17 at 07:36
  • 1
    https://stackoverflow.com/a/609754/4108884 -> I believe this answers some of your questions. – Samuil Petrov Sep 12 '17 at 06:26
  • 2
    In which browser? – Ry- Sep 12 '17 at 06:31
  • 2
    Can you repro on this fiddle? https://jsfiddle.net/yqc92zqd/ – Kaiido Sep 12 '17 at 07:27
  • 1
    Have a look at [what `remove` actually does which makes it slow](https://stackoverflow.com/q/27341937/1048572) – Bergi Sep 20 '17 at 18:32

5 Answers5

2

I'm assuming you have bad performance on IE.

I'd say that's because the $.empty method calls removeChild in a loop for every removed element in the table and that method always had bad performance in IE - see this article for short case study with some performance comparisons and resolutions.

No matter what you do with large DOM you will have bad performance on IE. This article on appendChild in large DOM on IE is an example of how the community is still befuddled by this simple fact. :)

sztrzask
  • 89
  • 11
  • Actually this was not in IE but happened both on Chromium and Firefox ESR – Kemeia Sep 14 '17 at 10:26
  • Could you please give exact versions of both? Because they were plagued with the same issues regarding `removeChild` and `addChild` in the past. – sztrzask Sep 14 '17 at 11:54
  • Firefox ESR 45.9.0 and Chromium 57.0.2987.98 Thanks – Kemeia Sep 14 '17 at 12:01
  • Could you create a fiddle or codepen? There used to be quite a number of bugs on either browser, e.g. Chrome's `removeChild` being [very slow on css-heavy page.](https://bugs.chromium.org/p/chromium/issues/detail?id=499349) I cannot really reproduce this - it's ~200ms for me on Chrome. – sztrzask Sep 15 '17 at 08:40
  • you have a point - i tried to create a fiddle that reproduces the scenario but it doesn't reproduce. digging further in my code i see that if i remove a class that makes all td elements floats, the problem is also resolved. i tried to reproduce the latter but didnt manage either - i need to dig some further – Kemeia Sep 18 '17 at 08:50
  • I made a fiddle with floating elements [https://jsfiddle.net/kjLwykv3/4/](https://jsfiddle.net/kjLwykv3/4/) based on @Kaiido fiddle. I couldn't find a culpirit in the Chrome's profiler though - it just says that the .remove is taking 10x more time than without floats, without any subcalls. So I made the [v5](https://jsfiddle.net/kjLwykv3/5/) where the floats are not added to the document just to make sure. Basically changing the HTML with floating elements is supposed to trigger a reflow by the browser where the elements sizes and positions will be recalculated as per the float specification. – sztrzask Sep 28 '17 at 11:12
  • But for some reason it's silent and not showing in the profiler, nor batching. – sztrzask Sep 28 '17 at 11:12
1

If you ask why emptying the table should take 1000 times longer than appending it, well, it just shouldn't: that is a bug.

You can find important performance differences depending on the browser, its version, the version of jQuery or the OS, but 25s vs 23ms is way too much. It seems than somewhere you hit a O(N^2) operation. For less than that people open tickets and the browser's developers acknowledge them.

Anyway, removing elements its not always faster that adding them. Our intuition says that destroy should be faster than create but that's not necessarily true when it comes to manipulate the DOM. Both jQuery and the browser need to make additional work to take elements out.

If you have a look to the current (3.2.1) implementation of jQuery append() function you will see that what it basically does is to use the native JS method appendChild(). On the other hand jQuery empty() function (html() uses empty()) needs to loop over all the inner elements and take care of removing the related data (e.g. events) to each of the inner elements to prevent memory leaks.

And, if you use just vanilla JS, the browser still needs to check a big bunch of things when removing elements. If you want to get into the browser's internals you can check this Firefox issue.

It doesn't matter if you go for removeChild(), innerHtml or textContent(), the browser always need to recalculate styles and layout: check this one in Chromium.

This kind of bugs are common. If you search into the Chromium for issues with the keywords "removeChild" and "slow" you get a pretty long list. Sometimes they come and go due to fixes introducing regressions, like in this case involving floating elements. This other one can be specially interesting for you, because it proofs that some bugs are not connected with the method you choose to remove the elements and because, like in your case, it shows up when complex CSS rules are applied to that elements (suggesting than the browser triggered a reflow).

I was unable to reproduce your issue neither in Firefox nor in Chrome, so I can't address the exact reason for the 25 seconds or 1.5 minutes, but it really seems to a bug fixed in more recent versions. If you still have the same old version and can reproduce it, it could be worth to check if removing the elements in inverse order (starting from lastChild) helps. Perhaps you avoid recalculating the position of all the floating siblings after the one you remove.

Next time maybe you want to open a ticket and follow how they find the bug and fix it. It will satisfy your curiosity and you can learn a lot about browser internals!

David
  • 5,090
  • 2
  • 21
  • 42
  • Thanks for this elaborate answer! You were right, it was a Chromium specific bug. I'm using Chromium 57.0.2987.98, on Firefox it doesn't happens. The bug you linked with removeChild on elements with a lot of CSS seems to draw a very similar issue, only that it seems to have been fixed. – Kemeia Oct 16 '17 at 06:46
  • @Kemeia. I was specifically looking in the issue tracker for a bug like yours between versions 57 and 61 (mine, where it is working smooth), but I couldn't find it. Maybe it got fixed as part of other issue, maybe it was never reported. If you update to 61 or newer and still have problems maybe the problem is somewhere else. Let me know if that happens and we can dig further on it! – David Oct 16 '17 at 07:05
0

Instead of looping though entire DOM remove and re-render again you can use hide() and show(). Which is fast and good for performance since retrieving the hidden column is also very easy. Please find the snipet useful.

//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>
karthik
  • 1,040
  • 9
  • 21
0

You can clear the table by assigning its inner HTML the value of the empty string.Then it will not go through each of the row element to find header,tbody or row to delete it.Hence would be quicker than your current approach.

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

0

You just wanted to know why. But maybe someone else wants to know a way for speedings things up... Therefore I just want to point out what i encountered.

I had an issue with removing elements fast enough which I created dynamically before (was part of an svg animation... kind of). My solution was to store the elemens which have been created before in an array. Later for the removal I just had to iterate through the array to delete them. that was much faster, and did the trick for me. In the end for my case it was not the performance of the .remove() (or .removeChild()) function itself but the DOM scanning for the designated elements (as mentioned in Davids answer) which slowed down the process.