Faster JavaScript Trim

Since JavaScript doesn't include a trim method natively, it's included by countless JavaScript libraries – usually as a global function or appended to String.prototype. However, I've never seen an implementation which performs as well as it could, probably because most programmers don't deeply understand or care about regex efficiency issues.

After seeing a particularly bad trim implementation, I decided to do a little research towards finding the most efficient approach. Before getting into the analysis, here are the results:

Method Firefox 2 IE 6
trim1 15ms < 0.5ms
trim2 31ms < 0.5ms
trim3 46ms 31ms
trim4 47ms 46ms
trim5 156ms 1656ms
trim6 172ms 2406ms
trim7 172ms 1640ms
trim8 281ms < 0.5ms
trim9 125ms 78ms
trim10 < 0.5ms < 0.5ms
trim11 < 0.5ms < 0.5ms

Note 1: The comparison is based on trimming the Magna Carta (over 27,600 characters) with a bit of leading and trailing whitespace 20 times on my personal system. However, the data you're trimming can have a major impact on performance, which is detailed below.

Note 2: trim4 and trim6 are the most commonly found in JavaScript libraries today.

Note 3: The aforementioned bad implementation is not included in the comparison, but is shown later.

The analysis

Although there are 11 rows in the table above, they are only the most notable (for various reasons) of about 20 versions I wrote and benchmarked against various types of strings. The following analysis is based on testing in Firefox, although I have noted where there are major differences in IE6.

  1. return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    All things considered, this is probably the best all-around approach. Its speed advantage is most notable with long strings — when efficiency matters. The speed is largely due to a number of optimizations internal to JavaScript regex interpreters which the two discrete regexes here trigger. Specifically, the pre-check of required character and start of string anchor optimizations, possibly among others.
  2. return str.replace(/^\s+/, '').replace(/\s+$/, '');
    Very similar to trim1 (above), but a little slower since it doesn't trigger all of the same optimizations.
  3. return str.substring(Math.max(\S/), 0),\S\s*$/) + 1);
    This is often faster than the following methods, but slower than the above two. Its speed comes from its use of simple, character-index lookups.
  4. return str.replace(/^\s+|\s+$/g, '');
    This commonly thought up approach is easily the most frequently used in JavaScript libraries today. It is generally the fastest implementation of the bunch only when working with short strings which don't include leading or trailing whitespace. This minor advantage is due in part to the initial-character discrimination optimization it triggers. While this is a relatively decent performer, it's slower than the three methods above when working with longer strings, because the top-level alternation prevents a number of optimizations which could otherwise kick in.
  5. str = str.match(/\S+(?:\s+\S+)*/);
    return str ? str[0] : '';

    This is generally the fastest method when working with empty or whitespace-only strings, due to the pre-check of required character optimization it triggers. Note: In IE6, this can be quite slow when working with longer strings.
  6. return str.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');
    This is a relatively common approach, popularized in part by some leading JavaScripters. It's similar in approach (but inferior) to trim8. There's no good reason to use this in JavaScript, especially since it can be very slow in IE6.
  7. return str.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, '$1');
    The same as trim6, but a bit faster due to the use of a non-capturing group (which doesn't work in IE 5.0 and lower). Again, this can be slow in IE6.
  8. return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
    This uses a simple, single-pass, greedy approach. In IE6, this is crazy fast! The performance difference indicates that IE has superior optimization for quantification of "any character" tokens.
  9. return str.replace(/^\s*([\S\s]*?)\s*$/, '$1');
    This is generally the fastest with very short strings which contain both non-space characters and edge whitespace. This minor advantage is due to the simple, single-pass, lazy approach it uses. Like trim8, this is significantly faster in IE6 than Firefox 2.

Since I've seen the following additional implementation in one library, I'll include it here as a warning:

return str.replace(/^\s*([\S\s]*)\b\s*$/, '$1');

Although the above is sometimes the fastest method when working with short strings which contain both non-space characters and edge whitespace, it performs very poorly with long strings which contain numerous word boundaries, and it's terrible (!) with long strings comprised of nothing but whitespace, since that triggers an exponentially increasing amount of backtracking. Do not use.

A different endgame

There are two methods in the table at the top of this post which haven't been covered yet. For those, I've used a non-regex and hybrid approach.

After comparing and analyzing all of the above, I wondered how an implementation which used no regular expressions would perform. Here's what I tried:

function trim10 (str) {
	var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
	for (var i = 0; i < str.length; i++) {
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(i);
	for (i = str.length - 1; i >= 0; i--) {
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(0, i + 1);
	return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';

How does that perform? Well, with long strings which do not contain excessive leading or trailing whitespace, it blows away the competition (except against trim1/2/8 in IE, which are already insanely fast there).

Does that mean regular expressions are slow in Firefox? No, not at all. The issue here is that although regexes are very well suited for trimming leading whitespace, apart from the .NET library (which offers a somewhat-mysterious "backwards matching" mode), they don't really provide a method to jump to the end of a string without even considering previous characters. However, the non-regex-reliant trim10 function does just that, with the second loop working backwards from the end of the string until it finds a non-whitespace character.

Knowing that, what if we created a hybrid implementation which combined a regex's universal efficiency at trimming leading whitespace with the alternative method's speed at removing trailing characters?

function trim11 (str) {
	str = str.replace(/^\s+/, '');
	for (var i = str.length - 1; i >= 0; i--) {
		if (/\S/.test(str.charAt(i))) {
			str = str.substring(0, i + 1);
	return str;

Although the above is a bit slower than trim10 with some strings, it uses significantly less code and is still lightning fast. Plus, with strings which contain a lot of leading whitespace (which includes strings comprised of nothing but whitespace), it's much faster than trim10.

In conclusion…

Since the differences between the implementations cross-browser and when used with different data are both complex and nuanced (none of them are faster than all the others with any data you can throw at it), here are my general recommendations for a trim method:

  • Use trim1 if you want a general-purpose implementation which is fast cross-browser.
  • Use trim11 if you want to handle long strings exceptionally fast in all browsers.

To test all of the above implementations for yourself, try my very rudimentary benchmarking page. Background processing can cause the results to be severely skewed, so run the test a number of times (regardless of how many iterations you specify) and only consider the fastest results (since averaging the cost of background interference is not very enlightening).

As a final note, although some people like to cache regular expressions (e.g. using global variables) so they can be used repeatedly without recompilation, IMO this does not make much sense for a trim method. All of the above regexes are so simple that they typically take no more than a nanosecond to compile. Additionally, some browsers automatically cache the most recently used regexes, so a typical loop which uses trim and doesn't contain a bunch of other regexes might not encounter recompilation anyway.

Edit (2008-02-04): Shortly after posting this I realized trim10/11 could be better written. Several people have also posted improved versions in the comments. Here's what I use now, which takes the trim11-style hybrid approach:

function trim12 (str) {
	var	str = str.replace(/^\s\s*/, ''),
		ws = /\s/,
		i = str.length;
	while (ws.test(str.charAt(--i)));
	return str.slice(0, i + 1);

New library: Are you a JavaScript regex master, or want to be? Then you need my fancy XRegExp library. It adds new regex syntax (including named capture and Unicode properties); s, x, and n flags; powerful regex utils; and it fixes pesky browser inconsistencies. Check it out!

181 thoughts on “Faster JavaScript Trim”

  1. Great work Steve. Grats on blowing the pants off the competition. Parsing the Magna Carta 20 times in less than a millisecond is flagrantly badass indeed.

  2. Thanks, Shady. But really, the competition is regular expression engines themselves and how to best take advantage of them. I doubt many JavaScripters who’ve written a trim function have spent much time considering alternative approaches.

  3. After looking at your comparison of trim methods (which in my oppinion is quite interesting) I found minor bugs in your trim10 and trim11 methods. It seems that your array indices and arguments to substr are off by 1. Consider this, for a hopefully correct trim11:
    function trim( str ) {
    str = str.replace(/^\s+/, ”);
    for( var i = str.length-1; i > 0; –i ) {
    if( /\S/.test( str[i] ) ) {
    str = str.substring( 0, i+1 );
    return str;

  4. Fixed. Thanks.

    By the way, I’d recommend avoiding treating strings as arrays to look up character indexes like that (e.g., str[i]). It’s not part of the ECMA-262 3rd Edition standard and doesn’t work correctly in IE. Better to stick with the charAt method for accurate lookups.

  5. Thanks, Doeke! And thanks for mentioning that Dean Edwards referenced this in Base2, as I hadn’t known that previously. (By the way, through referrer logs I’ve so far discovered this post also referenced in implementations by or discussions with the developers of jQuery, Ext, Rails, and CFJS.)

    For other readers, see Doeke Zanstra’s blog post for performance times of the above trim functions in several WebCore, Gecko, and Presto based browsers on Mac OS, and a better version of trim10.

  6. Just wanted to let you know Steve, that since being pointed at your blog by Dean Edwards, that its been added to the list of RSS feeds for our News Aggregator (Planet Dojo) at

    Great article and very interesting articles all around on your blog. Keep up the great work!


  7. Interesting. My firefox actually ran trim10 the fastest. Constantly return near 0ms times. A bug perhaps, or was it just that speedy?

    If I cranked it up to 60 iterations, it would show 10ms.

    Regardless, very nice work.

  8. Jeff, it’s just that speedy. 🙂 With the given test of trimming the Magna Carta with small amounts of whitespace at both ends, trim10 certainly should be the fastest of those shown here.

    But as this blog post explains in some detail, none of these are the one-true fastest. Their comparative speeds depend largely on the data they’re fed, although some are more consistent than others cross-browser and/or with any given data.

    Trim10’s strength is that, apart from edge whitespace, the length of the string has very little impact on its performance. Its weakness is strings which contain large amounts of leading or trailing whitespace, since a loop over each character won’t traverse that nearly as fast as a simple regex. That’s why I recommended trim11 over trim10, as although it’s a little slower than trim10 with many types of strings, it removes one of trim10’s weaknesses vs. the regex-based functions (long, leading whitespace).

  9. True, but in all honesty, I’ve never really run into cases in the wild where the trimming that needed to be done was that out of hand.

    Sure, if I make up data to be cleaned and stuff the snot outta it with spaces, it pokes a hole in the looping method — but I’ve never encountered that IRL.

    So for me, I’ll happily gank trim10 😀 Thanks!

  10. Shortest notation:

    function trim(str)
    str = str.replace(/^\s+/, ”)
    for (var i = str.length; i–;)
    if (/\S/.test(str.charAt(i)))
    return str.substring(0, ++i)
    return str

  11. Hi Steven, great article!
    I gave it a try and wrote my own trim.. if you are interested, here’s an implementation, I tried to make it work as fast as yours.
    In my PC it is sometimes a bit faster, sometimes equal.
    I created a 27k long string, with 300 spaces on each side, using less than that, both were always giving 0ms….
    Here’s the example:

    And here’s the piece of code:

    var trim = (function(){
        var ws = {},
            chars = ' \n\r\t\v\f\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
        for(var i = 0; i < chars.length; i++ )
            ws[chars.charAt(i)] = true;

        return function( str ){
            var s = -1,
                e = str.length;
            while( ws[str.charAt(--e)] );
            while( s++ !== e && ws[str.charAt(s)] );
            return str.substring( s, e+1 );

  12. Hi,

    Good thinking and nice research!

    I was curious about already existing trim implementations and your article sums them up very nicely. Still, playing myself a little bit and being the regex lover that I am, I came up with the simpliest implementation that I could think off and I was wondering what you think about it.

    String.prototype.trim = function() {

    return this.replace(/^\s*(\S.*\S)?\s*$/, ‘$1’);


    I haven’t test for speed or browser compatibility issues (I have just to support Firefox 2 & IE7 at the time being) and I’m not *very* concerned about a couple of ms difference so it could very well not be an alternative at all. Still, I’d love to hear what you have to say about it 😀

  13. Thanks, Nacho! Re: your implementation, it is not equivalent to the others shown (in other words it’s broken). Two reasons:

    – It will not work with strings which have just one non-whitespace character.
    – Since JavaScript doesn’t have a “single-line” mode you need to replace the dot with [\S\s] (or similar).

    In any case, /^\s+|\s+$/g is already more “simple” if you measure by readability (arguably) or number of characters.

  14. You completely got me there. I’m glad I posted it 🙂

    It’s of course true, it wouldn’t validate a one non-whitespace character string. Silly that it could scape my attention.

    About the “single line” mode I’m not so sure I follow you. I thought JavaScript had a flag (m) for multiline matching and therefore assumed that when the flag is not present matching only takes place on a single line strings … then again I do not know the internals as far as you do …

    I guess I’ll have to give 1 or 11 a go 😉

    Thanks for the comment!

  15. The regex terms single-line and multi-line are confusing for many people, which is why I shun them in RegexPal in favor of the more descriptive “^$ match at line breaks” (instead of “multi-line”) and “dot matches newline” (instead of “single-line”). However, “dot matches newline” mode is not available natively in JavaScript… it’s provided by XRegExp.

    In other words, without XRegExp, JavaScript provides no way for the regex dot to match all characters including newlines. Multi-line mode changes the meaning of the “^” and “$” tokens — it has nothing to do with what the dot matches.

  16. Hey Steve, excellent analysis. Thought I’d throw in my two cents here; after slogging through it, I saw Ariel already posted a near-verbatim version of the trim10 redux I’d written. 🙂
    I did a bit of extensive testing based on your test example using variations of #10 and #11. Like you said, while #10 is nice and zippy when there’s no leading space, it’s just too slow when there’s any significant leading space. So I went with #11, and here’s what I ended up with. (Brevity first, as always! 🙂 )

    function trim13 (str)
    var str = str.replace(/^\s*/, “”), s = /\s/, i = str.length;
    while (s.test(str.charAt(–i)));
    return str.substring(0, i + 1);

    Just putting the /\S/ regex (from #11) outside of the loop sped it up for starters, about twice as fast on runs of 10000 times. A notable quirk I found with the /^\s+/ regex: it performs much worse (3400ms vs. 470ms) on strings with no leading whitespace, compared to strings with even a single leading space. Perhaps you could explain that better, but it seems /^\s*/ keeps a consistent performance in both cases.

    On a different note, we seem to share the same taste in alcohol. 🙂 Vodka + Red Bull is my mainstay at any bar, but my vodka of choice happens to be Grey Goose. And I drink entirely too much Red Bull – 4 to 6 cans on a usual workday. Keeps you running, no? ~_^

Leave a Reply

Your email address will not be published. Required fields are marked *