Move this topic
faster jQuery.trim()
Implemented
- Maybe later
- Under review
- In-progress
- Implemented
- Will not implement
in Developing jQuery Core
•
11 years ago
Here's a patch for a faster version of jQuery.trim based on native String.trim and these improvements: http://blog.stevenlevithan.com/archives/faster-trim-javascript
Test case: http://jsbin.com/oquji/2
Patch:
(removed)
I also just requested a pull request.
The native String.trim is ridiculously fast.
5
Replies(40)
Re: faster jQuery.trim()
11 years ago
While I think using the native trim() makes sense if it's available, I wonder about the need for the Levithan enhancement. How representative of a use case is trimming the Magna Carta? Seems most of the time the text to be trimmed would be much smaller than 27,600 characters.
Have you noticed performance problems with jQuery's $.trim()? Have you isolated and benchmarked it? If so, it would probably be helpful to see your tests.
Cheers,
--Karl
Re: Re: faster jQuery.trim()
11 years ago
Agreed, trimming strings that long probably doesn't happen often.
Here's what I did for testing:
Using this page: http://jsbin.com/oquji/2
And this ~300 char input string 50,000 times:
Here's my results:
- Firefox 3.5: jQuery.trim: 1400ms, new trim: 8ms
- Chrome 4.0.249.43: jQuery.trim: 373ms, new trim: 272ms
- IE8 32bit: jQuery.trim: 2239ms, new trim: 1777ms
- IE8 64bit: jQuery.trim: 2737ms, new trim: 1726ms
- Safari 4.0.4: jQuery.trim: 3511ms, new trim: 144ms
I tried with some smaller strings as well, for Firefox and Safari, the new trim is always faster, for Chrome it's only faster for ~120 characters or more and for IE8 the new code is faster for ~50 characters or more.
(Update: added Safari and char length tests)
Leave a comment on kswedberg's reply
Re: faster jQuery.trim()
11 years ago
Also, if you've got a string that long, it's probably an HTML string. You can safely $(html) it anyway...unless you bust the heap!
Leave a comment on ajpiano's reply
Re: faster jQuery.trim()
11 years ago
If the new code I've committed is overkill, then how about this simple improvement then:
Aside from simpler code than my commit it performs better on smaller strings and while not as fast on larger strings as my original, it is still faster than the current method.
Re: Re: faster jQuery.trim()
11 years ago
@travis : thanks for the heads up ...
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
@DBJ
The problem with the rtrim regex is that it has the global flag and will not be as fast as calling the two replaces. Maybe rtrim can be replaced (or supplemented) with an rtrimstart = /^\s\s*/ and rtrimend = /\s\s*$/ ?
I realize that /^\s+/ and /\s+$/ are simpler regexes but they are also slower in some cases. Steven Levithan's link above goes into some detail on that. Here's a new test case that includes all the permutations so far:
Be sure to try it with different text lengths and in a bunch of different browsers. Looks like we've got about a 10x - 1600x(!) improvement in speed over the "old" .trim() depending on string length so far.
Thanks!
--Travis
Leave a comment on travis.hardiman's reply
Re: faster jQuery.trim()
11 years ago
@Travis, you did (before) premature optimization, which is "the source of all evil" ... But, now when we have a proper javascript we can optimize.
var thtrim3 = typeof "".trim === "function" ? function( text ) {
return ! text ? "" : text.trim();
}
: function ( text ) {
return ! text ? "" : text.replace( /^\s\s*/, "" ).replace( /\s\s*$/, "" );
};
Above you have replaced rtrim in "my" method and arrived to above faster version. Which is OK. I am wondering will it trim-out "\u00A0" , as current offical regexp does ?
rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g ; // current jQuery
I am also wondering will it be able to cache the regular expressions used ? Also, is this extreme optimization necessary? In real programs, you will never find string trimming called 5000 times in a tight loop. Or strings which are longer than approx 1024. I am pretty sure that in the region of string length < 1024, one will see very little speed difference between browsers, using any of the trims from here. And one will se negligible difference v.s. all that repeated with the current jQuery.trim() .
Perhaps one can deliver a plugin/replacement, to be used by people who happen to deal with strings where length > 1024? Maybe then, this could be a better solution :
- // replacement for trimming Very Large Strings
- // for hosts which do not support ES5 "".trim()
- jQuery.trim = typeof "".trim === "function" ?
- function( text ) { return ! text ? "" : text.trim(); }
- : function (str) {
- // http://blog.stevenlevithan.com/archives/faster-trim-javascript
- var str = str.replace(/^[\s\xA0]*/, ''),
- ws = /[\s\xA0]/, i = str.length;
- while (ws.test(str.charAt(--i)));
- return str.slice(0, i + 1);
- }
Only to be used when and if needed ...
--DBJ
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
I was wondering about the \u00A0 as well. In all of my testing it was already matched by the \s. Do you know if that was to fix a specific browser bug? UPDATE: it does appear to be an IE bug, doh! Updated my jsbin...
When cached into variables, the 2 replaces perform better than the single global replace when the string length is longer than 7 chars in IE8, 4 in IE6, 0 in Safari, and 40 in Opera.
http://jsbin.com/oquji/10 Updated with regex variations: the simplest and fastest regexs that work are /^[\s\xA0]+/ and /[\s\xA0]+$/.
A plug-in would be fine for the Levithan mega-optimized version.
When cached into variables, the 2 replaces perform better than the single global replace when the string length is longer than 7 chars in IE8, 4 in IE6, 0 in Safari, and 40 in Opera.
http://jsbin.com/oquji/10 Updated with regex variations: the simplest and fastest regexs that work are /^[\s\xA0]+/ and /[\s\xA0]+$/.
A plug-in would be fine for the Levithan mega-optimized version.
Re: Re: faster jQuery.trim()
11 years ago
Above I added ("my" latest trim ) as dbjtrim2.
Under IE, it "comes out" as Steven L. "no regexp" version.
And it seems it always wins .... among all this highly researched and optimized regular expression based solutions.
But,it turns out: not. It actually does not win. This one wins across the browsers:
var rtrimleft2 = /^[\s\xA0]+/;
var rtrimright2 = /[\s\xA0]+$/;
var thtrim5 = typeof "".trim === "function" ? function( text ) {
return ! text ? "" : text.trim();
}
: function ( text ) {
return ! text ? "" : text.replace( rtrimleft2, "" ).replace( rtrimright2, "" );
};
var rtrimright2 = /[\s\xA0]+$/;
var thtrim5 = typeof "".trim === "function" ? function( text ) {
return ! text ? "" : text.trim();
}
: function ( text ) {
return ! text ? "" : text.replace( rtrimleft2, "" ).replace( rtrimright2, "" );
};
This should/might speed up jQuery 1.4.1 ? John , this seems as a "no brainer" ? (if such a thing exists)
@Travis: good work!
--DBJ
--DBJ
Leave a comment on travis.hardiman's reply
Re: faster jQuery.trim()
11 years ago
@DBJ
I think there might be a bit of a bug in IE in this latest version (related to \xA0?):
Original length: 314
jQuery.trim: 2834ms (length: 286)
DBJ trim 2: 954ms (length: 312)
DBJ's + travis's trim (fixed regex): 1280ms (length: 286)
DBJ's + travis's trim (fixed alt 1): 1439ms (length: 286)
DBJ's + travis's trim (fixed alt 2): 2320ms (length: 286)
DBJ's + travis's trim (fixed alt 3): 2273ms (length: 286)
Aside from that it looks like a decent speed improvement... but then I tried testing with some shorter strings and it seemed to be slower:
Original length: 16
jQuery.trim: 452ms (length: 10)
DBJ trim 2: 879ms (length: 10)
DBJ's + travis's trim (fixed regex): 354ms (length: 10)
DBJ's + travis's trim (fixed alt 1): 340ms (length: 10)
DBJ's + travis's trim (fixed alt 2): 573ms (length: 10)
DBJ's + travis's trim (fixed alt 3): 485ms (length: 10)
Re: Re: faster jQuery.trim()
11 years ago
@Guest : yes I noticed it too. I fixed it. It is the same IE related bug which makes \u00A0 significant.
It seems thtrim5() is a winner ... I tried in FF, IE, Opera, Safari and Chrome.
Leave a comment on Guest's reply
Re: faster jQuery.trim()
11 years ago
I've added a new commit on GitHub if that helps:
http://github.com/dieseltravis/jquery/commit/56dec13e279fbb7c06155e93c2f5ff5d073489ce
Thanks for helping @dbjdbj!
--Travis
http://github.com/dieseltravis/jquery/commit/56dec13e279fbb7c06155e93c2f5ff5d073489ce
Thanks for helping @dbjdbj!
--Travis
Re: Re: faster jQuery.trim()
11 years ago
FWIW, it looks like the existing jQuery.trim() tests would be adequate:
http://github.com/dieseltravis/jquery/blob/master/test/unit/core.js#L203
--Travis
http://github.com/dieseltravis/jquery/blob/master/test/unit/core.js#L203
--Travis
Leave a comment on travis.hardiman's reply
Re: faster jQuery.trim()
11 years ago
I have no problem with reliability, I am thinking of checking how much faster "everything" else would be?
What tool(s) should we use for that ?
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
I actually haven't been able to find any instances of jQuery.trim() being used anywhere inside the library itself. The only place that comes close is this:
http://github.com/dieseltravis/jquery/blob/master/src/manipulation.js#L454
I imagine that jQuery UI and plugins might be using it though. Like these for example
:
http://www.google.com/search?q=%22jQuery.trim%28%22+site%3Aplugins.jquery.com
http://www.google.com/search?q=%22jQuery.trim%28%22+site%3Ajqueryui.com
http://ejohn.org/blog/jquery-livesearch/
There are these that could be tested:
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L629
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L939
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L1463
Although this would probably have to be updated I think:
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L1125
--Travis
http://github.com/dieseltravis/jquery/blob/master/src/manipulation.js#L454
I imagine that jQuery UI and plugins might be using it though. Like these for example
:http://www.google.com/search?q=%22jQuery.trim%28%22+site%3Aplugins.jquery.com
http://www.google.com/search?q=%22jQuery.trim%28%22+site%3Ajqueryui.com
http://ejohn.org/blog/jquery-livesearch/
There are these that could be tested:
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L629
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L939
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L1463
Although this would probably have to be updated I think:
http://github.com/dieseltravis/jquery/blob/master/speed/jquery-basis.js#L1125
--Travis
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
Yes, I have also noticed with a bit of a surprise, that jQuery.trim() is actually not used inside jQuery ...
Although, inside jQ, trimming "from left" is done on several places, and (I am guessing here) trimming "from right".
So perhaps we could have jQuery.rtrim() and jQuery.ltrim() ? Provided they will be used/needed ?
ES5 has no ltrim/rtrim so perhaps this makes them usefull too...
PS: I guess jQuery.trim() will have to stay because of the legacy...
--DBJ
--DBJ
Leave a comment on dbjdbj's reply
Re: Re: faster jQuery.trim()
11 years ago
Neat.
Detail: I think NBSP (\xA0) happens to be an issue only in IE RegExp (all of them).Maybe some insignificant additional neatness can be achieved by having
default option for IE and another default options for the others.
But maybe IE is not the only one, so we can imagine a little test to check this :
- var regex_nbsp_bug = "\xA0".replace(/\s/,"").length > 0 ;
And then use it :
- jQuery.fasterTrim.defaultOptions = regex_nbsp_bug
- ? {
- trimLeft: /^[\s\xA0]+/
- }
- : {
- trimLeft: /^\s+/
- } ;
Who knows, maybe one day all of Your trim-work gets noticed ...
--DBJ
PS: why and how is all of the text underlined I have no clue. I could not make it not-underlined ...
PPS: on 24.Feb all of the text was underlined, on 26-th it is not underlined. Only in editing mode it is still underlined.
PPS: on 24.Feb all of the text was underlined, on 26-th it is not underlined. Only in editing mode it is still underlined.
Leave a comment on travis.hardiman's reply
Re: faster jQuery.trim()
11 years ago
Brilliant, I'll update the code as soon as I get a chance, thanks!
--Travis
--Travis
Re: Re: faster jQuery.trim()
11 years ago
Updated again with Dave's suggestions to use separate functions instead of options, also added .trimAttr().
Plugin , demo , source .
--Travis
Plugin , demo , source .
--Travis
Leave a comment on travis.hardiman's reply
Re: faster jQuery.trim()
11 years ago
Nice one Travis : Triming Jamboree 2010 ;o)
Also, I hate when and if I am being petty ... perhaps ltrim() and rtrim() might come usefull to someone ?
Even if they are called triml() and trimr().
The key message here is the difference the string length makes (multiplied with number of browsers).
Also. $.extend() raised it's "ugly head" on this one, which I find fascinating too.
--DBJ
hasNativeTrim = String.hasOwnProperty( "trim" )
works, until some "Joker" environment does this :
String.prototype.trim = "Joker", the above would not work.
I would personally do :
hasNativeTrim = "function" === typeof "".trim
Also, I hate when and if I am being petty ... perhaps ltrim() and rtrim() might come usefull to someone ?
Even if they are called triml() and trimr().
The key message here is the difference the string length makes (multiplied with number of browsers).
Also. $.extend() raised it's "ugly head" on this one, which I find fascinating too.
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
Ah ok, I think I misunderstood how .hasOwnProperty() worked, I'll switch back to the typeof code. Thanks.
Firefox implements native .trimLeft() and .trimRight() so those could be used, although they aren't part of the ECMA standard.
I also discovered that IE actually misses a bunch of white-space characters that every other browser matches correctly with \s. Although their infrequent use makes me wonder if it's even worth it to try to fix it. Try the \s test in IE: http://xregexp.com/tests/unicode.html
--Travis
Firefox implements native .trimLeft() and .trimRight() so those could be used, although they aren't part of the ECMA standard.
I also discovered that IE actually misses a bunch of white-space characters that every other browser matches correctly with \s. Although their infrequent use makes me wonder if it's even worth it to try to fix it. Try the \s test in IE: http://xregexp.com/tests/unicode.html
--Travis
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
I've landed the latest code along with some tests:
http://github.com/jquery/jquery/commit/141ad3c3e21e7734e67e37b5fb39782fe11b3c18
Thanks Travis and DBJDBJ for the work on this!
http://github.com/jquery/jquery/commit/141ad3c3e21e7734e67e37b5fb39782fe11b3c18
Thanks Travis and DBJDBJ for the work on this!
Leave a comment on jeresig's reply
Re: faster jQuery.trim()
11 years ago
In case you do ;o)
For native string trimming use native String.prototype.trim() function wherever possible.
Do not use String.trim().
It is not part of ES5. Browsers are not required to implement it
http://gist.github.com/329172
--DBJ
For native string trimming use native String.prototype.trim() function wherever possible.
Do not use String.trim().
It is not part of ES5. Browsers are not required to implement it
http://gist.github.com/329172
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
Interesting, I the Mozilla docs say otherwise, but they might be incorrect:
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/Trim
--Travis
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/Trim
Introduced in JavaScript 1.8.1
String.trim() is part of the ECMAScript 5 standard
--Travis
Re: Re: faster jQuery.trim()
11 years ago
I've already switched to using String.prototype.trim in jQuery:
http://github.com/jquery/jquery/commit/ba8938d444b9a49bdfb27213826ba108145c2e50
http://github.com/jquery/jquery/commit/ba8938d444b9a49bdfb27213826ba108145c2e50
Re: Re: faster jQuery.trim()
11 years ago
Just tried the latest Opera and that supports a native String.prototype.trim as well. So that will get the perf boost too.
Not that anyone uses Opera
--Travis
Not that anyone uses Opera

--Travis
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
The only implementation/definition I could find in the ES5 spec is :
Also quick check arround major browsers, conforms that only FF has String.trim() implemented.
--DBJ
15.5.4.20 String.prototype.trim ( )
Also quick check arround major browsers, conforms that only FF has String.trim() implemented.
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
Ah, you're right I just got to that part of the spec:
Good find!
--Travis
15.5.4.20 String.prototype.trim ( )
The following steps are taken:
1. Call CheckObjectCoercible passing the this value as its argument.
2. Let S be the result of calling ToString, giving it the this value as its argument.
3. Let T be a String value that is a copy of S with both leading and trailing white space removed. The definition of white space is the union of WhiteSpace and LineTerminator.
4. Return T.
NOTE The trim function is intentionally generic; it does not require that its this value be a String object. Therefore, it can be transferred to other kinds of objects for use as a method.
Good find!
--Travis
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
To test for the ES5 string trim behaviour, in as many browsers, OS-es and devices you can, please browse to : http://jsbin.com/omata/6
ES5 trim does *not* throw an exception if not-a-string is used as its argument. It simply transforms the argument to the string by calling toString() method, on the argument itself.
I suppose we want jQuery trim to take care of that too? And in the spirit of jQuery forgiving nature, I suppose we want to return empty string on both null and undefined given as arguments ? Call me a "pest" but I think that currently John takes care only of an null argument ?
function( text ) {0return text == null ?0"" :0trim.call( text );0} :0// Otherwise use our own trimming functionality
Perhaps by doing this :
function( text ) {0return ! text ? // covers null, undefined and empty string0"" :0trim.call( text); // text.toString() called internally} :0// Otherwise use our own trimming functionality
We might cover all the "obviously bad" input, and let the inbuilt trim take care of the rest? Or.
We might *not* let through anything that is not-a-string :
function( text ) {0return "[object String]" !== Object.prototype.toString.call(text) ? // only strings please!0"" :0trim.call( text); 0} :0// Otherwise use our own trimming functionalityThis last version, will provide additional cross browser robustness, because not every "modern" browser implements ES5 string trim equally. For example in W7+Safari we have :TypeError: Result of expression 'String.prototype.trim' [undefined] is not an object.
Take a pick. And thanks for persevering ... ;o)
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
We already cover all these cases - just look at the tests in the test suite:
http://github.com/jquery/jquery/commit/141ad3c3e21e7734e67e37b5fb39782fe11b3c18#diff-2
It's passing in Firefox, Safari, Chrome, and IE. Doing == null covers both null and undefined. We cover this in the core style guidelines:
http://docs.jquery.com/JQuery_Core_Style_Guidelines
http://github.com/jquery/jquery/commit/141ad3c3e21e7734e67e37b5fb39782fe11b3c18#diff-2
It's passing in Firefox, Safari, Chrome, and IE. Doing == null covers both null and undefined. We cover this in the core style guidelines:
http://docs.jquery.com/JQuery_Core_Style_Guidelines
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
I think that "legacy" jQuery users need to be informed that (for example) this :
http://jsbin.com/omata/13
ilustrates an ES5 native trim behaviour. Which in turn might influence their legacy code using jQuery,trim() ...
--DBJ
http://jsbin.com/omata/13
ilustrates an ES5 native trim behaviour. Which in turn might influence their legacy code using jQuery,trim() ...
--DBJ
Re: Re: faster jQuery.trim()
11 years ago
Yes? And nothing has changed save that jQuery.trim(false) now returns "false" rather than "" and that we no longer throw a method not found exception for non-strings (both obvious bug fixes). Returning "" for null and undefined, instead of a serialization of the window object, makes much more sense (and is something that we've always done).
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
"... And nothing has changed...", well it seems that jQuery.trim() behaviour has changed (a lot) ?
Returning a result of toString() for non strings from trim() is what ES5 dictates. And this is what the latest jQuery.trim() will do. And this is what jQuery.trim() was *not* doing ever before. I just think this has to be made clear in the api docs, same as it was made clear for new behaviour regarding parsing illegal JSON strings.
For example, maybe someone was relying on the fact that jQuery.trim() will throw an exception. It will not any more and her code might not work any more with 1.4.3 . I hope I am clear now ?
Many thanks --DBJ
Re: Re: faster jQuery.trim()
11 years ago
That is a highly dubious argument, at best. Sure, when 1.4.3 is released I'll mention in the docs that it's now inline with the ECMAScript 5 trim with the exception of undefined and null.
Leave a comment on dbjdbj's reply
Re: faster jQuery.trim()
11 years ago
Of course it is "dubious". A real "edge case". But. In line with jQuery forgiving nature.
Otherwise, if I would be in your shoes, jQuery will be throwing exceptions left , right and center ;o)
Some might say thanks's god I am not ... ;o)
--DBJ
--DBJ
Leave a comment on dbjdbj's reply
Change topic type
Link this topic
Provide the permalink of a topic that is related to this topic
Reply to travis.hardiman's idea
{"z2566198":[14737000000767179,14737000000770887,14737000000771466,14737000000771521,14737000000772167],"z957804":[14737000000647350],"z464496":[14737000000647299],"z2951261":[14737000000647620,14737000000652036,14737000000659363,14737000000659375,14737000000666781,14737000000667721,14737000000672775,14737000000732171,14737000000763299,14737000000766713,14737000000768480,14737000000768502,14737000000768942,14737000000771504,14737000000771587,14737000000771817],"z3029194":[14737000000647280,14737000000647339,14737000000647377,14737000000645601,14737000000659038,14737000000665677,14737000000665753,14737000000672060,14737000000726839,14737000000735023,14737000000752894,14737000000762075,14737000000767183,14737000000767181,14737000000770881,14737000000781031,14737000000770885],"z-1":[14737000000659367]}

