Reputation: 5307
I have a large HTML table (1,000-1,500 rows, 40 cols wide). I have a few input and select boxes so users can filter rows. The relevant javascript/jquery (note: not entire code base is pasted in as it is not the bottleneck) attached to it looks like:
function autoRank() {
// auto number
rank = 0;
$("#myTablePlayers .playerData").each(function() {
if ($(this).css("display") != "none") {
rank++;
$(this).find('td').eq(colRank).text(rank);
}
});
}
var teamCols = $(),
GPCols = $(),
posCols = $(),
ageCols = $();
$("#myTablePlayers .playerData").each(function() {
var columns = $(this).find('td');
teamCols = teamCols.add($(".colTeam", this));
GPCols = GPCols.add(columns.eq(colGP));
posCols = posCols.add(columns.eq(colPos));
ageCols = ageCols.add(columns.eq(colAge))
});
function filterTable() {
// Need some error checking on input not number
minGP = $("#mingp").val()
teams = $("#teamFilter").val().toUpperCase()
position = $("#position").val()
age = $("#age").val()
$("#myTablePlayers .playerData").show();
/* Loop through to check for teams */
if (teams) {
teamCols.each(function() {
if (!this.innerHTML.toUpperCase().includes(teams)) {
$(this).parent().hide();
}
});
}
/* Loop and check for min GP */
GPCols.each(function() {
if ( Number(this.innerHTML) < minGP) {
$(this).parent().hide();
}
});
/* Check for age requirement */
if (age) {
age = Number(age)
ageCols.each(function() {
thisAge = Number(this.innerHTML);
if ( thisAge < age || thisAge >= age+1 ) {
$(this).parent().hide();
}
});
}
/* Check the position requirement */
if (position) {
posCols.each(function() {
var thisPos = this.innerHTML
if (position == "D") {
if (thisPos.indexOf("D") == -1) {
$(this).parent().hide();
}
} else if (position == "F") {
if (thisPos.indexOf("D") != -1) {
$(this).parent().hide();
}
} else if (thisPos != position) {
$(this).parent().hide();
}
});
}
autoRank();
}
When stripping down the code as minimal as possible, the offending code is the
var.each(function() { ...
in the filterTable()
function.
When I run this on Chrome or Firefox it runs quickly (sub 1 second) and the DOM is rendered properly. When I execute on Safari it takes 30+ seconds.
Why is this and what can I do to adapt for this browser?
jQuery: 1.11.1 (same issue even after upgrading to 3.1.1).
Safari: 10.0.1
Firefox: 50
Chrome: 54.0.
Upvotes: 1
Views: 791
Reputation: 338326
After removing all the repetition and unnecessary complication from your code, this is what remains:
var colRank = 0, colTeam = 1, colGP = 2, colAge = 3, colPos = 4;
function filterTable() {
var minGP = +$("#mingp").val();
var age = +$("#age").val();
var teams = $("#teamFilter").val().toUpperCase();
var position = $("#position").val();
var rank = 0;
$("#myTablePlayers .playerData").each(function () {
if (
(teams && this.cells[colTeam].textContent.toUpperCase().includes(teams)) ||
(minGP && +this.cells[colGP].textContent < minGP) ||
(age && (+this.cells[colAge].textContent < age || +this.cells[colAge].textContent >= age+1)) ||
((position === "D" || position === "F") && this.cells[colPos].textContent.indexOf(position) === -1) ||
(!(position === "D" || position === "F") && (this.cells[colPos].textContent !== position))
) {
this.cells[colRank].textContent = ++rank;
this.style.display = "";
} else {
this.style.display = "none";
}
});
}
I've already removed almost all jQuery in favor of native DOM manipulation.
The remaining .each()
could be tuned into a plain old for
loop over the document.getElementById('myTablePlayers').tBodies[0].rows
, if you want to squeeze out the last bit of possible performance.
Reorder the if
conditions by likeliness: From the one that will typically filter out the most rows to the one that will filter out the least rows. Because JS short-circuits conditions, this way less conditions are checked overall.
Making the table display: fixed
can also improve render performance at the expense of flexibility.
Finally, you can use CSS to do counters. This is probably faster than manually setting the contents of a table cell. Test for yourself.
Upvotes: 1