Benchmark tests: style vs. className

There are two ways of changing the style of an element: changing the element's style properties or changing its className. I feel that the second option should be a Best Practice, since it honours the separation of behaviour and presentation, where a style change doesn't. After all, changing the style of an element in JavaScript means that your script file contains presentation information. That is not right: presentation instructions should go in the CSS file.

I wanted to make sure that changing the className doesn't lead to performance problems. My new style vs. className benchmark test clearly shows that it doesn't. In fact, changing the className is faster than changing the style in all browsers but Safari.

I'm very glad of this outcome, since I can now solemnly declare changing the className whenever you want to change the styles of an element a Best Practice, not only from a theoretical point of view, but also from a practical one.

This is the blog of Peter-Paul Koch, mobile platform strategist, consultant, and trainer. You can also follow him on Twitter.
Atom RSS

I’m around at the following conferences:

(Data from Lanyrd)

Categories:

Monthlies:

Comments

Comments are closed.

1 Posted by Pingu on 10 July 2005 | Permalink

I absolutely agree that this is the best way to do it. Keep style out of JavaScript.

One thing I often do is add more than one style with className. Does this affect the times recorded?

I'm aggravated that className as a string seems an inefficient way to store a string of classes.
In addition, there seems no way in CSS to say: "if I have class X AND class Y then be like this", only "if my parent has X and I have Y". Perhaps multiple classes have been overlooked?

2 Posted by setmajer on 10 July 2005 | Permalink

Pingu,

You can chain css classes in selectors like so:

classOne.classTwo {
/* some rules */
}

The only trouble with this is that it's buggy in -- wait for it -- IE/Windows. :-(

You can also apply a class to an ID rather than an element, like:

#elementID.className {
/* some rules */
}

And that seems reasonably well suppoorted in all recent CSS-aware browsers.

3 Posted by Sebastian on 10 July 2005 | Permalink

Could you also add a test for runtimeStyle (only Internet Explorer). In pure humble logic this must be the fastest.

4 Posted by Robert Nyman on 10 July 2005 | Permalink

I remember doing some tests in IE 5/Mac where using multiple class names didn't work properly when changing them on-the-fly (i.e. there were some rendering bugs).

If I remember it correctly, it was just basic thing like background color etc, but I wanted one class and just to add an extra class for mouseOver events (kind of like the :hover CSS pseudo-class).

Anyone else seen this?

5 Posted by Freddie on 10 July 2005 | Permalink

The whole idea about CSS is that it is object orientated, and each object is an instance of the class. It is all based around inheritance. When browsers style an element they fetch all of its properties, first looking at the in-line style (style="/*stuff*/") then in the tags and last in imported style sheets. So, explicitly assigning values the style method should always be slower, except in KHTML, where as far as I can tell it just looks for all instances of a class, fetches its properties and then applies it inline.

6 Posted by Fuzztrek on 11 July 2005 | Permalink

I agree this should be a best practice... however in the next "version" of javascript/dom, can we aim to include some functions to facilitate this? Currently, modifying a className is much more clumsy than modifying a style. I use a combination of .replace and +=, but either way I need to do trivial checks to make sure I'm not adding duplicate classnames, or calling .replace on a string that doesn't exist, etc. I think some streamlining of the various methods to access the style of an element should also be considered, along with an alternative to "getElementById/getElementsByTagName" (such as something like dean edward's cssQuery perhaps), although I'd probably be happy with just getElementsByClassName ;)

7 Posted by Jacob on 11 July 2005 | Permalink

I agree that changing the class of an element is the most suitable way to change its style, however, I often change things like visibility (/display) using styles, and in some cases I think that's the best solution (it really doesn't need its own class).

8 Posted by Jeremy French on 11 July 2005 | Permalink

I wonder how much this will vary depending on the complexity of the stylesheet. Surely it will take longer to change on a page with many cascading rules?

9 Posted by Jo on 11 July 2005 | Permalink

What about when javascript is disabled, and the element has an initial style as visibility:hidden or display:none. The styled element stays hidden or does not exist.


To prevent this: The 'to-be-changed elements' styles can be added with the method document.write.

10 Posted by Aaron Barker on 11 July 2005 | Permalink

I understand the logic in using class names for changing presentation. But what about positioning of absolute elements? Since where they will be positioned could be based on the centering of the page, or other elements being hidden/shown... this will be a very dynamic number. So they can not be put into a class to be added later on.

Is there a way around this, or is this a loophole to the keeing style out of the structure argument?

11 Posted by Keith on 11 July 2005 | Permalink

Correct me if I'm wrong, but doesn't the use of className require that your classes be fixed and defined ahead of time? If you want to be able to change the style dynamically -- for instance, letting the user choose any color from a 24-bit palette -- wouldn't that require the use of style?

If you can actually *define* classes using the DOM (I admit I haven't looked into this myself), I think that another set of performance tests should take that into consideration -- in other words, compare *creating* and assigning className vs. just changing style.

Keith

12 Posted by Ted Drake on 11 July 2005 | Permalink

so, for those of us that are still novices at creating JavaScript, how should we begin modifying existing scripts?

For example, I'm a big fan of Zebra Tables from alistapart. However, I would love to see it change the row class instead of adding new styles.
What would you suggest? Could you create a tutorial on modifying existing scripts?
Thanks

13 Posted by Alan Bram on 12 July 2005 | Permalink

The previous comments about visibility (Jacob) and, to a lesser extent, positioning (Aaron Barker) solidified for me something that has been in the back of my mind. These things are not styles!

Is there any reason in the world to make something invisible, other than that under control of some script you intend to make it visible? It seems to me that script would be the *only* place manipulating the visibility makes sense.

It feels like it's a CSS attribute just because nobody could think of a better place to stick it. In other words, it's a bit of a kluge.

14 Posted by Pingu on 12 July 2005 | Permalink

Visibility I agree is much simpler with direct access - positioning is over-rated unless you're getting complicated.

It's better to avoid absolute positioning. But when it's needed, you can put the absolute div in a relative div which flows with the page. That way you can center etc, while keeping abs positioning - no script.

15 Posted by Tino Zijdel on 12 July 2005 | Permalink

I do remember that className-swapping was unworkable slow in old Mozilla versions, but indeed browsers have improved over time on this point.

As for the 'inefficiency' of classes being a white-space seperated string; it's easy to create your own method to deal with that - usually it shouldn't be more than 3 lines of javascript.

16 Posted by blatimer on 12 July 2005 | Permalink

One other possibility for changing the styles of an element is to set new style sheet rules.

Opera doesn't support new style sheet rules, but it allows you to create style elements that have the same effect.

I've created a sortable zebra table that works in Mozilla and Opera, but not IE, by counting the number of rows in a table and adding a new style rule on page load that will forever set the even and odd colors.

http://bdmarti.port5.com/zebraTable.html

I haven't been able to get it to work with IE, even with dean edwards' ie7. If you would like to see a horrible crash, uncomment the IE code in my zebreaTable script and try to load the page with IE.

While in this particular case IE support may not be possible, there may be other instances where setting a dynamically generated rule on startup is the most effective way to set styles...

maybe someone will find this script interesting or useful. if you can get it to work in IE, let me know.

17 Posted by Fuzztrek on 12 July 2005 | Permalink

"As for the 'inefficiency' of classes being a white-space seperated string"

I don't think it's inefficient at all - just inelegant.

On another note, I'm not sure, but I don't think ppk is suggesting we completely remove the ability to alter a style. I interpret "best practice" as meaning: "when at all possible, use this method".

18 Posted by Stephan Morais on 13 July 2005 | Permalink

In my experience, I've run into a number of cases (usually when triggered by a mouseover) where setting the className on an element was sluggish, and I had to resort to setting style properties. I suspect the difference between my situation and your test case has to do with the number of style classes defined in the css file. It would be interesting to rerun your tests with more classes, seeing how performance degrades.

19 Posted by ppk on 13 July 2005 | Permalink

A few hurried responses to comments. First of all, if you want to test something, do it, don't wait for me to do it.

runtimeStyle : how does it work?

There are a few exceptions to the general rule I outlined, but I haven't yet written them down. Display or visibility toggling and transitions (like sliding layers) will definitely be part of those exceptions.

Complexity of style sheet: don't know. Try it.

Classes defined ahead of time: definitely. If the user can make random changes, the best practice won't work. But that's not the most common application of style changes.

Begin modifying existing scripts: just make sure that the script changes the className, and not the style properties. If it already does that, the script is OK.

Setting style sheet rules: definitely possible, but not a best practice, as far as I'm concerned. Changing the className, or the style in one of the cases mentioned above, is much easier.

Cases where className changes were sluggish: example links?

20 Posted by Greg Dyke on 14 July 2005 | Permalink

Because the fact that the class attribute is a string, I created the following js to model the classes of an element as a set. Hope this is of use to someone

http://tirno.com/javascripts/classdealer.js

21 Posted by Alex Lein on 14 July 2005 | Permalink

@ppk
I'd like to show you something.
http://www.baka.ca/test/ie6_className_vs_style.jpg

I'm using a 2ghz p4 with 512MB of RAM, but 32MB is shared for video.

I remember having to change a lot of scripts to change the "style" instead of the "className" when I inherited the websites I work on.

Is it possible your test machines all have video cards?

22 Posted by ppk on 14 July 2005 | Permalink

Can you please do both tests five times and repost the screenshot? That's my criterium.

Changing style isn't that much slower than changing class name in Explorer, so I'm quite prepared to believe on some computers it will work faster.

And yes, obviously my test machines have a video card. If they didn't, I wouldn't be able to see anything .

23 Posted by Alex Lein on 14 July 2005 | Permalink

Ok, I re-ran these, and I discovered something interesting.

I ran the tests 5 times each:
http://www.baka.ca/test/ie6_CNvsS_inorder.jpg

Here, I alternated the tests:
http://www.baka.ca/test/ie6_CNvsS_unordered.jpg

I initial conclusions seem to be wrong. However, when running multiple style changes in a row, the className seems the right way to go. But when running them in "any order" as I would think a real-world example would be, then the performance margin narrows considerably.

24 Posted by ppk on 14 July 2005 | Permalink

That IS interesting.

A real-world example based on my proposed best practice will only use className, though.

25 Posted by Tino Zijdel on 14 July 2005 | Permalink

Indeed; a couple hundreds of seconds won't matter that match, and Opera is still the big loser when using style instead of className. I would however also be interested in more real-world examples (e.g. pages with a larger stylesheet and cascading rules).

26 Posted by Jo on 19 July 2005 | Permalink

To use style for Safari, other browsers use className. In this case, with visibility: hidden, it can be an important advantage that the style change effect is done as fast as possible .

.makeHidden {visibility:"hidden";}

if (/safari/i.test(navigator.userAgent)) {
// is safari
someElement.style.visibility = "hidden";
} // other than safari
someElement.className = "makeHidden";

27 Posted by Jo on 19 July 2005 | Permalink


should be: .makeHidden {visibility:hidden;}

28 Posted by Robert Nyman on 19 July 2005 | Permalink

This is something you might appreciate to get to know what Microsoft says about IE:

Faster DHTML in 12 Steps

With the quote:
...consider modifying the style object directly for the elements you wish to change, rather than modifying an element’s className property

29 Posted by Robert Nyman on 19 July 2005 | Permalink

Sorry, the link was stripped...
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/perf/dhtmlperf.asp

30 Posted by Maian on 20 July 2005 | Permalink

There's a flaw in this test. The script assumes that changing styles and classname are synchronous - that the script waits for them to finish before going to the next step. However, style changes are probably asynchronous - as Opera as shown, it's very inefficient to wait for style changes to render during the script.

The real test is the rendering speed of the style change, which this script fails to test. All that has been shown is that it's faster to set only one property instead of two - and that's not surprising at all. The only thing useful I can gather from this test is Opera's behavior.

31 Posted by ppk on 20 July 2005 | Permalink

Maian,

Please create a better test and post the link.

32 Posted by Igor E. Poteryaev on 20 July 2005 | Permalink

Sorry, but for Opera results are very bad in "style" case because of fontSize change intest.
I have commented out this line:
/*x[i].style.fontSize = '120%'; */

and re-run tests. Opera was MUCH more faster then before. I got [15 ms / 57ms]
ratio for [className/style]. "className" approach still faster, but your initial values was somehow misleading.

33 Posted by Rick on 21 July 2005 | Permalink

It would be nice if your comments were correct, but for most situations you are wrong. Changing classname on deep cascades causes the cascade chain on an item to be broken, purging all inherited styles on an item and fundamentally altering the contents.

Try running your tests against to change an item from a classname with one background to one with another where all the others attributes (padding, etc.) are set in parent elements. You'll find the styles of the parent elements get purged, in both FF and IE.

Nice idea, wish it worked this way, but it doesn't. Hence the reason many styles must be hard coded into js.

-rt

34 Posted by ppk on 21 July 2005 | Permalink

Rick,

Do you have any links to tests pages available that back up your claims?

35 Posted by ppk on 21 July 2005 | Permalink

Igor,

Interesting. Did the style change stay extremely slow when you commented out the background-color? Ie: was the cause definitely the font-size?

36 Posted by Igor E. Poteryaev on 22 July 2005 | Permalink

Well, I repeated tests commented out background-color instead of font-size.
The result was about 57ms. We can conclude that combination of change of both background-color and font-size make Opera slow. Funny, isn't it ?

37 Posted by ppk on 22 July 2005 | Permalink

I did the test with padding: 10px instead of the font-size, and Opera was still slow.

Then I changed background-color to text-decoration, and Opera was still slow.

The conclusion seems to be that changing two styles in Opera is very slow. One style can be changed very quickly, though.

38 Posted by Christian on 24 July 2005 | Permalink

I thought actually too that it is the better practice to use class changes for some dhtml effects. But i have had to return to the style changes, because that methode is much faster.

To be more precise it depense a lot from the current position from a element in the document flow. If e.g. a element is floated and it should just change background-color and color, then it's must faster to just change the styles directly. Changing the class from a such a element does require actually to reflow atleast all siblings because that change could affect also the flow property ( and e.g force a completly new rendering situation ).

On small screen devices such effects are remarkable without any benchmarking.

39 Posted by Maian on 25 July 2005 | Permalink

ppk, the flaw is in an unqualified assumption you're making - that the script waits for style changes to render. AFAIK, there's no way to test whether this is the case - with a JS script at least.

With all that said, the case for setting className really depends on the scenario. The disadvantages of using className are:

- They may be slower than direct style changes.
- They pollute the CSS "scope" with a new class name, which may be bad from the unobtrusive script point of view.
- It's easier to make unintentional changes (e.g. changing a selector from ".classA" to ".classB" will invalidate a selector ".classA div"). They can be worked around, but it can get annoying pretty fast.

I say leave className changes to scripts that modify the presentation of content. JS "behavior" scripts, like drag&drop, are better off using direct style changes.

40 Posted by ppk on 25 July 2005 | Permalink

Maian,

Prove it.

Frankly I'm getting a bit tired of all these comments that say I'm wrong, but that refuse to give any evidence.

Your assumption is unproven until you provide a link to a test page.

41 Posted by Maian on 25 July 2005 | Permalink

I don't have to prove any assumption - I'm not even making one! What I'm saying is that you're making an unqualified assumption, and the burden of proof lies on you to prove it.

Figured a way to test whether it's synchronous or not:

1) Create a function that does the same thing as one of the test functions, except the propert(ies) that are set are not related to rendering. For ex:

...
x[i].style.a = '#00cc00';
x[i].style.b = '120%';
...

2) Compare the performance of those two. If they are identical (or close enough), rendering is most likely asynchronous.

I constructed a test case along these lines and found out that rendering is indeed synchronous in IE6 and Fx, so your assumption is correct (for those two browsers).

However, your article must mention whether rendering is synchronous or not and how you proved it.

Another thing you need to prove: whether the number of properties in the rule matter when setting className. ex:

.class1 {
border: 1px solid green;
padding: 0.5em;
background-color: red;
font-size: 10pt;
z-index: 0;
margin: 5px;
}
.class2 {
border: 1px solid green;
padding: 0em;
background-color: blue;
font-size: 10pt;
z-index: 0;
margin: 5px;
}

Only 2 properties are changed - are the rest re-applied as well?

42 Posted by ppk on 1 August 2005 | Permalink

An interesting reply:

http://webkit.opendarwin.org/blog/?p=13

43 Posted by Matthias Krumm on 30 August 2005 | Permalink

can we say, that using className instead of style is only faster, if i change classes of maybe several hundred elements? so, when changing just one element at time, it makes no difference if i use className or style. is that true?

there are so many posts already - im sorry, if the answer is already there! :)