I have a rather big table where I dynamically remove some rows. It works, but it is very slow. Right now it takes approx. 1.5 seconds to remove 50 rows on IE8 and Firefox (almost no difference between the browsers).
I know that DOM manipulation is slow in general, but there must be a faster way to do this.
Right now, I'm using this syntax:
$("#myTable tr").slice(250, 300).remove();
The offsets in the slice() method may vary. I use slice() since this was recommended in jQuerys help and other methods to perform the same thing - like find() or eq() - where not faster. I read about doing an empty() before the removal, but that was even slower.
Consider using the actual javascript, in case jQuery is triggering render refreshes: http://jsfiddle.net/MbXX5/
var removeRows = function(ofTable,from,to) {
for(var row=to; row>=from; --row) {
ofTable.deleteRow(row);
}
};
As you can see in the jsfiddle, this is instant. Note that I'm traversing the array in reverse, so that the row numbers remain correct. There is a chance this improves the performance, depending on the DOM code and the JIT strategies the browser uses.
[Edit: new jsfiddle with colour-coded cells to make it really obvious which rows have gone]
The problem is that for every row that you .remove(), the table is redrawn by the browser. To make it faster, remove the table from the DOM, take out the lines and put the table back at its place.
$table = $("#myTable").clone(true,true);//First true to keep events, second true to deepcopy childs too. Remove it if you do not need it to make it faster.
$table.find("tr").slice(250,300);remove();
$("#myTable").replaceWith($table);
You can use filter but I don't think it will be faster
$("#myTable tr").filter(function(index){
return index > 250 && index < 300;
).remove();
The problem is the browser tries to update the screen view of the DOM on each row removal.
You can do it by one of
removing the table, from the document, removing all rows and after
that inserting it back
cloning the table, removing elements on the clone, replacing the table with the clone
or if the amount of rows remaining is less than the ones remove, you could create a new table, insert all the rows in that and replace the existing table with the new one
The main idea is for the table to not be attached to the DOM when you do the removals, this way it will only update the view once all the rows are removed.
Is it possible you add an ID to each row? And then select the rows directly by ID and removing the rows? Like so:
var el = document.GetElementById("RowID_1");
document.removeChild(el);
jQuery is on top of Javascript. I guess using javascript directly is faster.
edit:
Ofcourse you can create a loop like this:
for(i=250;i<=300;i++)
{
var el = document.GetElementById("RowID_" + i);
document.removeChild(el);
}
edit 2:
Hide the table while editing so the browser does not update after each removal ? ;)
Try this . i hope it will help you
$("#myTable tr").slice(250, 300).html('');
Related
I am looking to script a site that has a set of changing values. I am trying to figure out how to call the text value inside of only one of these entries at a time.
Any one of these can look like this
<tr ng-repeat="(key, game) in crash.games.slice().reverse()" class="" style="">
<td ng-if="::game.crash > 199" class="crashHighResult">RANDOM NUMBER THAT I WANT TO SEE</td>
tr is the parent of td and the text value inside of td is what I'm trying to see
The only problem I'm experiencing is that there are as many as 20 entries stored during this time and they can all have seemingly the same classes and parents as every other one, the only difference being the random number value...
I am thinking that If I can pull them all at the same time and then maybe create an array with those values I might be able to do what I need to do but I'm a bit stumped.
I am very new to javascript and jquery and this is a learning experience for me. Thanks for your help!
Your best bet would be to use document.querySelectorAll if you want to understand some native JS techniques. There are a couple of other ways of getting the elements you want (getElementsByClassName and getElementsByTagName) but they don't play nearly as well with forEach as does the former.
So, to grab the elements:
let cells = document.querySelectorAll('td');
And to loop over that nodelist:
cells.forEach(function (cell) {
console.log(cell.textContent);
});
At the moment the code just logs the text content (your random number) to the console, but you can get a feel for what you can now do with that data.
For example, to get the last (most recent?) random number (the last cell in the nodelist) you would use:
let rnd = cells[cells.length - 1].textContent;
Hope this helps you out.
If you need to select the first tr td in the table to get the value, using jQuery you can select all tr td elements and then specify the first one.
$("tr td").first().text();
This selector finds all tr td elements as it traverses the dom, so it will find the top row in the table first. The first() function returns a jquery object of the first element found. The text() gets whatever text is inside the td tags, basically the same as the js innertext or textcontent.
If you want to do processing with all the values you can use the jquery selector each() function.
$("tr td").each(function (index , element) {console.log($(element).text())})
This loops through all the elements and prints their values to the console, but you could modify the function to sum the values, put them in an array, or whatever.
After debugging my tables seemed to load slow (I assumed it was my server), I found that it was actually the front-end javascript, not the backend PHP. The server is responding in 3-4ms while the javascript handling is taking up to 350ms.
After reading this article, I found the culprit:
Article snippet:
var arr = reallyLongArray;
$.each(arr, function(count, item) {
var newTd = $('<td></td>').html(item).attr('name','pieTD');
var newTr = $('<tr></tr>');
newTr.append(newTd);
$('table').append(newTr);
});
The difference is I am using appendTo() instead of append. This is because my rows have dynamic jquery elements to them - click handlers, .data(), etc.
The solution in the article is basically to concatenate your rows and then run one .append() at the end instead of one for each row.
Is there a similar solution for appendTo()? Perhaps appending to some sort of ghost element and then inserting the whole element at the end? Would this increase performance?
Perhaps appending to some sort of ghost element and then inserting the whole element at the end?
Exactly. You can create your rows and append them to a disconnected tbody element, then append that tbody element to your table. That way there's a single live DOM manipulation, not hundreds of them.
When clearing HTML elements such as select boxes, tables, or lists, is it better/faster to remove the nodes (e.g., select.options.remove(i), table.deleteRow(i)) or just empty the innerHTML (e.g., select.innerHTML = "")? Or does it matter?
An example case would be reinitializing a table. A specific select field's value should load different values for a subsequent HTML table. When the select value changes, the table needs to be reinitialized.
In IE you cannot set the innerHTML of a select element. So for a cross-browser solution the only way is to add/remove child nodes.
I made a new test that isn't broken.
http://jsperf.com/innerhtml-vs-removechild/67
It's not perfect either since part of the sample setup is part of the test so this might skew the results.
This gives that innerHTML is faster but I don't know by how much.
Per this conversation here: What is the best way to empty an node in JavaScript
It appears that the while (elm.firstChild) {elm.removeChild(elm.firstChild);} approach got the the best results across browsers in this test.
(Would have put this as a comment instead of an answer, but the comments are coming in awful fast, so I didn't want it to get lost.)
Case 1:-
Run on 1000 Child DOM Element to Remove.
Which one is Faster?
Remove Child (5,676,264 ops/sec ±1.46%) --> Faster
innerHTML = "" (4,359,867 ops/sec ±1.46%) --> Slower
Case 2:-
Which one is able to remove event handler of child Nodes?
Remove Child --> Able to remove event handler
innerHTML = "" --> Not able to remove event handler
Result:-
Seems It's better to used Remove Child for removing any child nodes.
Ref:-
https://www.measurethat.net/Benchmarks/Show/6910/0/innerhtml-vs-removechild-vs-remove
https://rusingh.com/javascript-benchmark-removechild-vs-innerhtml/
I'm attempting to target the last parent table row within a table that has children table-row elements inside of it. I've tried the below jQuery to target the :last pseudo, however, like expected, it is targeting the absolute last table-row element within the targets parent table.
$('table[id*="dgRegistrantList"]').find('tr:last').addClass('EventRegLastAttendee')
I've put together a jsFiddle with the HTML block I'm attempting to target with the jQuery, I hope it is helpful!
http://jsfiddle.net/jodriscoll/LZA7e/
The Green table-row is the one I would like to target, however, the one highlighted in Red is the obvious one receiving the class.
This system can generate a variant of table rows depending on the users selection prior to this "Step". For a full example of what I'm working with, visit: http://secure.massgeneral.org/event-form (I'm working with Step 2).
Please be aware that the HTML I'm working with is produced by a CMS software that I as the customer, do not have access to changing. Hence the purpose of this jQuery exercise.
If all the parent <tr> elements have the classes BBListOddRowStyle or BBListEvenRowStyle you can do this:
$('table[id*="dgRegistrantList"]').find('tr[class*=RowStyle]:last')
.addClass('EventRegLastAttendee')
DEMO
If not, you can use .children() twice to make sure you target the right ones:
$('table[id*="dgRegistrantList"]').children('tbody')
.children('tr:last').addClass('EventRegLastAttendee')
DEMO
Use this code to target the last row:
$('table[id*="dgRegistrantList"]').find('tr[class^=BBList][class$=RowStyle]:last').addClass('EventRegLastAttendee')
Explanation:
tr //it will look for tr
[class^=BBList] //which class starts with BBList
[class$=RowStyle] //and ends with RowStyle (so we're leaving Odd and Even inside and not recognized)
:last //the last of those element, if you remove it you select all of them
Is .children() what you're looking to do?
$('table[id*="dgRegistrantList"]').children('tr:last').addClass('EventRegLastAttendee');
.children() only goes down one dom level, while .find() will go down as far as it can.
Don't use find. It will look at any depth and it may match unintended subtables. Perhaps it will work for your example, but you don't want to be acquiring a bad habit. Plus, find will be more costly than a targeted approach.
You want a more targeted approach:
var targetTd = $('table[id*="dgRegistrantList"]').children('tbody').children('tr:last').find('table:first').children('tbody').children('td:last');
use this code to target parent tr last row
$('table[id*="dgRegistrantList"]').find('tr[class^=BBList]:last').addClass('EventRegLastAttendee');
I want to backup an html table to afterwards filter it using jquery:
$('.row').closest('td').not(':contains(' + v + ')').parent('tr').remove();
Since I do remove() I have to back up the rows before:
var allTable = $('#mytable').html();
And then, when filter is performed I turn back to previous table data:
$('#mytable').html($(allTable));
But this does not work. If I do:
alert($(allTable).filter('tr').length);
next to the first assignment, zero rows are returned.
Please, can you assist me?
filter() is used to find elements within an array of elements. This isn't what you need. You're looking to find() the child elements within another. Also, storing the HTML only to turn it back in to a jQuery object is a little redundant - you may as well just store the jQuery object itself. Try this:
var $table = $('#mytable');
$table.remove(); // use your existing logic here
alert($table.find('tr').length);
$table.appendTo('body'); // add the table back in to the DOM when conditions are met
Example fiddle
I ran into a similar issue when using a highlight function. I solved it by cloning the table into a hidden div and restoring it from there, instead of from a variable. see jquery highlight() breaking in dynamic table
Did you solve this problem?
I suggest a workaround.
Instead of using your cloned table, make a (temporary) copy of it and use it for alert.
var alertTable = allTable;
alert($(alertTable).filter('tr').length);