jQuery sort causing iOS Safari to freeze
I have a page that is using jQuery to load an XML file, which I'm then outputting the contents of to the page.
Recently I added a sorting function to the output which is causing a 1+ or 2+ minute hang on Safari on an iPod Touch (depending upon how many fields I sort by) and a less than 1 minute hang on an iPad. The same sort returns within a few seconds on Firefox 4.0.1.
I'm afraid it's just a limitation of the iOS, but before I removed the sort, perhaps there's an optimization that could be made.
Before the filter there's 357 items in the XML. After the filter there's 199 items that are sorted开发者_运维技巧 through.
var videoGames = $($.parseXML(videoGameXml)).find("game");
videoGames = videoGames.filter(function (a) {
return ($(this).attr('addOn') != "true" && $(this).find('own').text() == "yes");
});
videoGames.sort(function (a, b) {
var firstTitle = $(a).find('title').text().toLowerCase();
var secondTitle = $(b).find('title').text().toLowerCase();
var firstSystem = ($(a).find("console").text() + " " + $(a).find("version").text()).toLowerCase();
var secondSystem = ($(b).find("console").text() + " " + $(b).find("version").text()).toLowerCase();
if (firstSystem != secondSystem) {
if (firstSystem > secondSystem) {
return 1;
} else {
return -1;
}
} else {
if (firstTitle > secondTitle) {
return 1;
} else if (secondTitle < firstTitle) {
return -1;
}
}
return 0;
});
videoGames.each(function () {
// runs quickly, so removed
});
Note that if I remove the system check as an initial 'optimization' that cuts the time about in half on the iPod Touch, but still results in the 1+ minute hang mentioned above.
So, is it an iOS device limitation, or can I optimize my sort?
Every time you do $(a) it will perform a very complex set of operations, so you better cache it. Also, you don't need the Title if System is different. This version should speed it up a bit:
videoGames.sort(function (a, b) {
var first = $(a);
var second = $(b);
var firstSystem = (first.find("console").text() + " " + first.find("version").text()).toLowerCase();
var secondSystem = (second.find("console").text() + " " + second.find("version").text()).toLowerCase();
if (firstSystem != secondSystem) {
if (firstSystem > secondSystem) {
return 1;
} else {
return -1;
}
} else {
var firstTitle = first.find('title').text().toLowerCase();
var secondTitle = second.find('title').text().toLowerCase();
if (firstTitle > secondTitle) {
return 1;
} else if (secondTitle < firstTitle) {
return -1;
}
}
return 0;
});
You could also cache the values in the object
Then, instead of:
var firstSystem = (first.find("console").text() + " " + first.find("version").text()).toLowerCase();
Do:
var firstSystem = first.data('system');
if (!firstSystem) {
firstSystem = (first.find("console").text() + " " + first.find("version").text()).toLowerCase();
first.data('system') = firstSystem;
}
You should move any selectors calls like this:
var firstTitle = $(a).find('title').text().toLowerCase();
out from the comparator function. The comparator function is supposed to be lightweight.
Either use children()
, next()
and the like or
scan you set once and create an array of keys upfront and then sort it using those keys.
The comparator function will be called 2n * ln(n)
times (depends on algorithm used) where n
is a number of elements in a set. So your code does the same expensive calculations twice at least.
精彩评论