Skip to main content

Bug Tracker

Side navigation

#5087 closed bug (patchwelcome)

Opened August 20, 2009 06:50PM UTC

Closed March 03, 2012 08:54PM UTC

Last modified October 08, 2013 10:07PM UTC

Use element.classList if available to speed up class manipulation

Reported by: sylvain Owned by: rwaldron
Priority: low Milestone: 1.next
Component: attributes Version: 1.3.2
Keywords: Cc: rwaldron
Blocked by: Blocking:
Description

Element.classList is a new API introduced in HTML5 for manipulating the classes of an element. The API is documented on https://developer.mozilla.org/en/DOM/element.classList.

This API is implemented in Firefox 3.6 and WebKit is planning to implement it.

JQuery could detect if this property is available and use it in the {add,had,remove,toggle}Class functions. A 3-90 speedup could be achieved with Firefox according to this benchmark: https://bug501257.bugzilla.mozilla.org/attachment.cgi?id=395627).

Attachments (0)
Change History (58)

Changed June 13, 2010 04:38PM UTC by dmethvin comment:1

component: unfiledselector

Changed November 13, 2010 04:05AM UTC by rwaldron comment:2

owner: → rwaldron
status: newassigned

Changed November 19, 2010 04:11PM UTC by rwaldron comment:3

milestone: 1.41.5
priority: minorlow

Changed December 30, 2010 04:18AM UTC by paul.irish comment:4

#7378 is a duplicate of this ticket.

Changed December 30, 2010 04:20AM UTC by paul.irish comment:5

blockedby: → 5766

Changed January 31, 2011 10:35PM UTC by MT comment:6

Apparently element.classList is currently implemented in both Firefox (3.6+) and Chrome (at least 8.0.552.237+). Two independent implementations are quite good reason to use this native feature in jQuery to improve performance.

Changed January 31, 2011 10:42PM UTC by rwaldron comment:7

There has been extensive research into the practical application of using classList internally - we're not in any rush to wrap two very different logic scenarios for a small perf boost

Changed February 01, 2011 04:04PM UTC by MT comment:8

Replying to [comment:8 rwaldron]:

''Small'' boost? Do you have some exact numbers? What do you think about benchmark results mentioned in the initial topic message? Thanks.

Changed February 01, 2011 04:54PM UTC by rwaldron comment:9

@MT I probably shouldn't have used a quantifying adjective.

I'm revisiting the benchmarks I wrote last fall.

Changed February 01, 2011 05:42PM UTC by rwaldron comment:10

_comment0: When you make classList do what jQuery does (which is what it would have to do if it was used in the implementation), with regard to multiple classes, its not faster: \ \ http://jsperf.com/classlist-vs-addclass1296582255773821

When you make classList do what jQuery does (which is what it would have to do if it was used in the implementation), with regard to multiple classes, its not faster:

http://jsperf.com/classlist-vs-addclass

I'd like to note that when I wrote a 5 class, 100 element test, the classList functions were faster in some cases.

Changed February 01, 2011 11:11PM UTC by MT comment:11

Replying to [comment:11 rwaldron]:

Thanks, this is interesting. Nevertheless, adding multiple classes to one element ''at once'' is quite rare (since just useless) operation. So, such benchmarks are not enough representative.

At the outside, classList might be used for adding single class, while pure script implementation—for adding multiple classes.

Changed February 01, 2011 11:21PM UTC by rwaldron comment:12

Is it really worth it? To add code that checks for support of classList and checks if the split string has length > 1 then use classList...? Seems like a lot of work

Trust me, I'm the biggest fan of classList and even proposed a new function call classlist() - that was faster in many regards.

Changed February 04, 2011 02:47PM UTC by MT comment:13

Replying to [comment:13 rwaldron]:

Is it really worth it? To add code that checks for support of classList and checks if the split string has length > 1 then use classList...? Seems like a lot of work

From practice perspective, I believe that adding single class is used far most often than adding multiple classes at once. So, if using native classList will result in ''any'' performance boost with single class case, then why not? ;-) We can even accept small perfomance loss in multiple class adding operation at all (to exclude an additional check for whether one or multiple classes are added) since this is rarely used.

proposed a new function call classlist() - that was faster in many regards.

What is the function you are talking about? Some potential jQuery.classlist()? Can you provide a details?

Changed February 04, 2011 03:59PM UTC by rwaldron comment:14

_comment0: Have you looked that jQuery UI source? Some examples: \ \ {{{ \ jquery.ui.tooltip.js, L37 \ addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content") \ \ jquery.ui.tabs.js, L 146 \ addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) \ \ jquery.ui.tabs.js, L 160 \ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ); \ \ jquery.ui.tabs.js, L 161 \ this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); \ \ jquery.ui.tabs.js, L 162 \ this.lis.addClass( "ui-state-default ui-corner-top" ); \ \ jquery.ui.tabs.js, L 163 \ this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ); \ \ Even more... \ jquery.ui.tabs.js, L 210, 214, 283, 291, 300, 307, 419 \ \ \ \ }}} \ \ \ You get the idea ;)1296836106072084

Have you looked that jQuery UI source? Some examples:

jquery.ui.tooltip.js, L37
addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content")

jquery.ui.tabs.js, L 146
addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )

jquery.ui.tabs.js, L 160
this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );

jquery.ui.tabs.js, L 161
this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );

jquery.ui.tabs.js, L 162
this.lis.addClass( "ui-state-default ui-corner-top" );

jquery.ui.tabs.js, L 163
this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );

Even more... 
jquery.ui.tabs.js, L 210, 214, 283, 291, 300, 307, 419



You get the idea ;)

As for the classlist() method I had previously been campaigning for:

https://github.com/rwldrn/jquery-classlist

Changed February 11, 2011 06:45PM UTC by paul.irish comment:15

There's a pull req for this now: https://github.com/jquery/jquery/pull/228

Changed February 15, 2011 07:28PM UTC by MT comment:16

Replying to [comment:15 rwaldron]:

When native feature is available, it is the ''only'' true way to do things and is preferred over pure script implementation anyway. Native implemenation is at least much more likely to work correctly than pure-script one.

For example, boolean attribute selector ('INPUT[autofocus]') in jQuery works only in browsers that support native querySelector() (see bug 5637). And this is not because native feature is ''faster'' or not at all, but because this is ''native''.

An exception for preferring native implementation is when this is ''entirely'' (in ''any'' use case) and ''considerably'' (by more than, say, 1.5x) slower than pure-script one. (Though, even in that case, native implementation may get faster in future versions of browsers while pure-script one will most likely not.)

As for classList() method you've mentioned, then, in my opinion, this might only ''return'' list of element classes. For ''adding'' multiple classes as JS-array, just regular addClass() should be used (impossibility to add classes as Array object is actually drawback of current addClass() method).

Thanks.

Changed March 05, 2011 08:01PM UTC by rwaldron comment:17

#8447 is a duplicate of this ticket.

Changed March 31, 2011 05:01AM UTC by danheberden comment:18

keywords: → pullreq
milestone: → 1.next

Changed April 12, 2011 07:58PM UTC by john comment:19

resolution: → patchwelcome
status: assignedclosed

See my comments in the closed pull request (classList appears to be too slow right now). If classList gets faster, or if a better patch is proposed, we'll happily land it.

Changed April 12, 2011 07:59PM UTC by john comment:20

component: selectorattributes

Changed September 03, 2011 03:33AM UTC by rwaldron comment:21

#10196 is a duplicate of this ticket.

Changed September 03, 2011 02:21PM UTC by anonymous comment:22

Changed September 03, 2011 02:30PM UTC by rwaldron comment:23

resolution: patchwelcome
status: closedreopened

I'm going to re-open this for possible 1.8 proposal.

Changed September 04, 2011 07:22PM UTC by addyosmani comment:24

cc: → rwaldron

Some quick comments: looking through the thread it would appear that the consensus was that for getting jQuery to do with classList what we currently do with multiple classes it was considered too slow to use. This also applied to the pull request that came through a few months ago.

Are there particular performance tests or implementations we're currently aware of that perform better than previously observed or is the intention of the discussion to seek a better implementation that works around what vendors currently have so that we can support this?

(just curious) :)

Changed September 04, 2011 07:37PM UTC by rwaldron comment:25

I'm just going to revive local branches I have and run perf tests. Thees a few things I want to try this time around

Changed September 08, 2011 03:59AM UTC by dmethvin comment:26

I'd leave the ticket closed unless there's a clear reason to revive it based on new benchmarks for example.

Changed September 20, 2011 05:34PM UTC by timmywil comment:27

keywords: pullreqpullreq, 1.8-discuss
status: reopenedopen

Changed December 12, 2011 05:00PM UTC by rwaldron comment:28

keywords: pullreq, 1.8-discuss1.8-discuss

The performance issue of setting multiple classes still exists.

Changed December 12, 2011 07:21PM UTC by rwaldron comment:29

keywords: 1.8-discuss

Changed March 03, 2012 08:54PM UTC by dmethvin comment:30

resolution: → patchwelcome
status: openclosed

OK, closing this yet again because it provides no performance benefit. Let's leave it closed until there is a pull request that shows it can make things faster without a lot of extra code, since it definitely will introduce a completely different code path.

Changed March 05, 2012 04:17AM UTC by rwaldron comment:31

I'll be paying attention here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=13999

If it can bring the multi-class piece of the puzzle to the table, I will start looking at this again.

Changed March 19, 2012 05:19PM UTC by rwaldron comment:32

@paul_irish just pinged me about some perf improvements happening here https://bugs.webkit.org/show_bug.cgi?id=80376#c2

Changed April 11, 2012 09:42PM UTC by rwaldron comment:33

Changed April 11, 2012 09:46PM UTC by dmethvin comment:35

Well, the 10x improvement boiled down to 2x at our API level; not nearly as impressive once you apply the jQuery semantics like multiple class add/remove.

Changed July 07, 2012 07:38PM UTC by dmethvin comment:36

#12027 is a duplicate of this ticket.

Changed January 17, 2013 06:13PM UTC by m_gol comment:37

IE9 is now the only browser supported by jQuery 2.0 that doesn't have the classList API implemented. Too bad, otherwise the argument about two code paths wouldn't be so severe as they would be two code paths in two separate jQuery versions.

What's your current position on this?

Current perf results: Chrome Canary: 2x faster, IE10: 2.35 faster, Fx 18 (current stable): 3.8 times faster when using classList. IMHO it's worth a shot.

Changed January 18, 2013 05:14AM UTC by rwaldron comment:38

The position is: until it supports _multiple_ classes at the same speed as jQuery, it remains a non-starter.

"me too" doesn't make specs update faster.

Changed March 02, 2013 10:46PM UTC by m_gol comment:39

I was really curious if spec imperfection is the blocker here so I implemented classList utilization in attributes.js (with fallback to className for IE9). Here's the pull request:

https://github.com/jquery/jquery/pull/1190

I also created a jsFiddle and run it in a couple of browsers:

http://jsperf.com/classlist-v-old-way/5

As you see, the only case where we get a (really slightly) worse performance is with addClass("many class names here"), the rest is significantly faster with the modified code. The most significant difference is with hasClass and also when adding/removing single classes (which is one of the most common uses, I suppose).

Here's a test case with 10 classes instead of 100:

http://jsperf.com/classlist-v-old-way/7

The difference is still significant.

I think it's worth to pull this patch, what do you think?

Changed March 02, 2013 11:02PM UTC by rwaldron comment:40

Thanks for the contribution, but jQuery won't use classList until it can be used to completely replace the current code

Changed March 03, 2013 02:25PM UTC by m_gol comment:41

_comment0: Since my patch won't go into the core and I think it can still be useful in cases where one focuses mostly on modern browsers, I created a jQuery extension redefining class methods to utilize classList:[[BR]] \ https://github.com/mzgol/jquery.classList \ \ I removed checking for classList support so the code won't work in IE9 but one can use a polyfill for classList for this browser (or just filter my code out by conditional comments for IE<10) \ \ I also modified test cases to utilize this code:[[BR]] \ http://jsperf.com/classlist-v-old-way/8[[BR]] \ http://jsperf.com/classlist-v-old-way/10 \ \ Both these test suites confirm performance gains up to 10x in some cases. Have fun!1362320782696002

Since my patch won't go into the core and I think it can still be useful in cases where one focuses mostly on modern browsers, I created a jQuery extension redefining class methods to utilize classList:

https://github.com/mzgol/jquery.classList

I removed checking for classList support so the code won't work in IE9 but one can use a polyfill for classList for this browser (or just filter my code out by conditional comments for IE<10)

I also modified test cases to utilize this code:

http://jsperf.com/classlist-v-old-way/8

http://jsperf.com/classlist-v-old-way/10

Both these test suites confirm performance gains up to 10x in some cases. Have fun!

Changed March 04, 2013 02:15PM UTC by m_gol comment:42

Good news: multiple arguments for classList.add & remove are already supported in WebKit nightly and Chrome stable. Unfortunately, no such change for toggle as the second argument is reserved for the boolean flag...

I updated my little project to detect support & account for that.

Changed March 04, 2013 03:38PM UTC by rwaldron comment:43

@m_gol can you do me a favor and create a fiddle demonstrating the updated implementation and link it here? This will be helpful for us to periodically revisit the API progress :D

Changed March 04, 2013 04:25PM UTC by m_gol comment:44

Two jsFiddles I pasted before contain test cases utilizing the latest version of my patch. I'll keep them up to date in README.md so you can just bookmark:

https://github.com/mzgol/jquery.classList

If you have any suggestions to test cases or anything else related to my patch, feel free to ping me.

Glad to help!

Changed March 04, 2013 05:39PM UTC by m_gol comment:45

Mozilla bug reports:

https://bugzilla.mozilla.org/show_bug.cgi?id=814014 - about accepting multiple parameters to add/remove

https://bugzilla.mozilla.org/show_bug.cgi?id=814090 - about toggle accepting a boolean as the second parameter

Changed March 04, 2013 09:09PM UTC by m_gol comment:46

WebKit bug report:

https://bugs.webkit.org/show_bug.cgi?id=111307 - div.classList.add('a', 'a') creates duplicate entries

Is it OK to post such updates here? It seems on topic but I don't want to spam people with excessive notifications.

Changed March 04, 2013 11:16PM UTC by rwaldron comment:47

Please do!

Changed March 05, 2013 03:36PM UTC by dmethvin comment:48

Yes, it's good to have a list of related bugs here, thanks m_gol!

As for the patch, I think we're still comfortable with this being in a plugin that shimmed .addClass() since it would be very rare that className manipulation is a performance issue and the additional code paths create testing/maintenance/debugging issues. Especially while the browsers have bugs in their implementations.

Changed March 08, 2013 12:13PM UTC by alnitak comment:49

_comment0: I've just created a plugin that replaces .hasClass, .addClass and .removeClass with versions that use element.classList if available, and which fallback to the original functions for the more unusual cases (i.e. multiple classes, function parameters, etc). \ \ https://gist.github.com/raybellis/5115997 \ 1362744862032496

I've also created a plugin that replaces .hasClass, .addClass and .removeClass with versions that use element.classList if available, and which fallback to the original functions for the more unusual cases (i.e. multiple classes, function parameters, etc).

https://gist.github.com/raybellis/5115997

Changed April 17, 2013 05:40PM UTC by m_gol comment:50

Changed July 30, 2013 10:22AM UTC by s@safejava.com comment:51

The biggest reason I can see to use classList, aside from the 200-300% speedups we're seeing in jsperf, is the elimination of the really awful performance implications of addClass() and removeClass() on large lists. For example, the naive way to remove all elements with class '.selected' from a long list would be $('.item').removeClass('selected') which invalidates the style of every single .item div, causing the next layout to be very expensive.

It's important to see the performance differences in the context of the subsequent layout, as not many of the tests thrown around show it. I've put together a jsperf that should illustrate my meaning.

http://jsperf.com/classlist-remove-vs-jquery-2

Changed July 30, 2013 01:22PM UTC by dmethvin comment:52

Just checking for the class actually being there has a positive effect. I'm also not sure why the two cases you've identified (removeClass with better selector) are so different.

Changed August 12, 2013 03:13AM UTC by jdunck comment:53

Replying to [comment:52 s@…]:

I've added a ticket (and a related pull request) to avoid assigning to className if the resulting value is the same.

http://bugs.jquery.com/ticket/14250

I think this would avoid the specific case described here (where most of the elements shouldn't be touched).

The biggest reason I can see to use classList, aside from the 200-300% speedups we're seeing in jsperf, is the elimination of the really awful performance implications of addClass() and removeClass() on large lists. For example, the naive way to remove all elements with class '.selected' from a long list would be $('.item').removeClass('selected') which invalidates the style of every single .item div, causing the next layout to be very expensive. It's important to see the performance differences in the context of the subsequent layout, as not many of the tests thrown around show it. I've put together a jsperf that should illustrate my meaning. http://jsperf.com/classlist-remove-vs-jquery-2

Changed October 01, 2013 11:11PM UTC by m_gol comment:54

Changed October 08, 2013 09:10PM UTC by dmethvin comment:55

N.B., I think the reason for at least some of the perf differences were that until 1.11/2.1 jQuery reassigned the className property and most browsers force layout on that *even if the classes are unchanged*. So that's what is behind my puzzlement in comment 53 above.

It's also highly likely likely that any needed reflow would totally overwhelm a performance benefit from going native here, no? The jsperf may not be reflecting this critical issue.

Changed October 08, 2013 09:48PM UTC by rwaldron comment:56

It would be great if folk just stopped being obsessed with this ticket. As owner, I've committed to keeping up to date with the native implementation and jQuery is still faster. I haven't forgotten, it's not being ignored and we're not going to have multiple code paths.

Changed October 08, 2013 09:54PM UTC by m_gol comment:57

@rwaldron Hey, I'm not obsessed about anything. When I asked 7 months ago in a comment if it's OK to post here links to bug reports related to the topic both you and Dave advised me to do it. I can stop if you changed your mind...

Changed October 08, 2013 10:07PM UTC by dmethvin comment:58

@rwaldron it's because of the recent tweets about it, don't worry. My thoughts on this haven't changed; even if there's a 2x speedup there's no evidence that it makes a meaningful performance difference in real code, and the resulting layout will be overwhelmingly larger than the work required to change the className string. Plus, there's m_gol's plugin for people who are trying to update thousands of classNames per second and finding it too slow.