jQuery: x800 faster .clean() function
Sunday, February 1st, 2009As I mentioned in my previous post, we’ve noticed severe performance problems when loading large chunks (660KB) of HTML via an .ajax() call.
Short story short, .load calls .clean() which uses .trim() which sucks. In our case it was taking around 2.7s!
So how can we get rid of .trim()? Here’s the first attempt:
// Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(); + var tags = elem.replace(/^\s+/, '').toLowerCase();
This is great as it takes the code from 2700ms to about 440ms.
But we can do a lot better! Why are we doing a .toLowerCase() on a 660KB string just to test the first 4-5 chars?! Also, we use indexOf() that will have to scan the entire 660KB string just to see if the string begins with a given string — and we do that a 6-7 times!
Let’s get rid of all that unnecessary work:
// Trim whitespace, otherwise indexOf won't work as expected
- var tags = jQuery.trim( elem ).toLowerCase();
+ var tags = elem.replace(/^\s+/, '').substring(0, 10).toLowerCase();
var wrap =
// option or optgroup
@@ -906,11 +906,12 @@
if ( !jQuery.support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
- var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
+ var hasBody = /<tbody/i.test(elem);
+ var tbody = !tags.indexOf("<table") && !hasBody ?
div.firstChild && div.firstChild.childNodes :
// String was a bare <thead> or <tfoot>
- wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
+ wrap[1] == "<table>" && !hasBody ?
div.childNodes :
[];
That brings it down to about 4ms! And I’ll bet you it makes the browser “feel” lighter too as it uses a lot less memory.
Size impact is tiny as well. The simpler first try adds just 6 bytes to the minimized version, or only 4 bytes to the gziped version. The second try adds 17 bytes on top of the first one (in the minimized version), or only 12 bytes to the gziped version.
You can track this patchset in ticket 4037.