Multiple Classes in IE

Monday, June 4, 2007 at 11:57 pm | Comments off

I'm going to explain the use of multiple classes, and where IE6 chokes. When writing CSS, I find that it is often very nice to use multiple classes, so you can have a base CSS class to set up some default styles and then add an additional class to add more meaning. This is a technique I often use, setting up my base module as div.box and then adding additional classes to that base CSS to create new modules. Take this CSS, for instance:

So, if I have <div class="box featured">, the classes for div.box and div.box.featured will be applied to the div, giving it a "featured" style.

div.box div.box

div.box.featured div.box.featured

Working with IE6

IE6, however, has an interesting caveat with multiple classes. IE6 does not make a distinction between the following rules:

How these rules should work is like this:

div.box.featured applies to <div class="box featured">
div.featured applies to <div class="box featured"> OR <div class="featured">

However, in IE6, div.box.featured works the same way as div.featured. IE6 doesn't understand the chain of classes within a CSS selector, but rather only reads the last class. So, IE6 reads div.box.featured as div.featured, while Firefox and IE7 will only apply the "featured" styles if the div also has a class of "box". For the most part, this isn't a big problem, as usually when you apply multiple classes, you want them all to apply. Let's look at an example of where this goes wrong. In this example, I have a table with zebra rows, and also want a darker "selected" state if the checkbox is checked.

Table with zebra rows and selected states in Firefox / IE7 Table with zebra rows and selected states in Firefox / IE7.

Table with zebra rows and selected states in IE6 Table with zebra rows and selected states in IE6.

The HTML would look something like this:

And this is the CSS:

IE 6 does not understand the difference between td.selected and td.alt.selected, meaning your selected state cannot have zebra rows, using this technique. You would have to create them as separate classes, .selected and .selected_alt, which means you would have to do more computing with you JS or PHP (or other server side language).

Update

I've created a demo of the zebra rows example.

Comments

Kev
June 5th, 2007
1:28 AM | #

Great article. I can see how multi-class selectors might add a lot of power to CSS, particularly in helping to minimize the use of wrappers. If only it were more reliable. I wonder if there might be a way to patch this using some tricky ie6-specific javascript or css?

Matt Turner
June 5th, 2007
3:54 AM | #

If you don't want to use JS (I appreciate this example does anyway) then the only solution is to un-optimize your html. In this instance you either add the selected class to the td giving you

tr td.selected {background-color: #d3edfb;}
tr.alt td.selected {background-color: #b1dff7;}

Or use unique class names

tr.selected {}
tr.altselected {} // notice no dot, once class name

Jens Meiert
June 5th, 2007
10:47 AM | #

I only skimmed this post - sorry - but did you really make sure that the problem's not rather related to the "overwritten" "background" properties? If you'd use different properties for different (combined) classes, everything should be fine ... Let's not feed the myth that IE doesn't support combined/multiple classes anymore.

It's still a nasty implementation problem, sure.

Ryan Brill
June 5th, 2007
10:54 AM | #

Jens, please read the article. It is not a myth that IE(6) does not fully support multiple classes. In the article, I say what it does and does not support.

Janos Hardi
June 5th, 2007
11:00 AM | #

I thought you will write a hack to misplace this annoying 'feature' of IE6 :)
I think it is relatively well-known problem, but your delightful examples are good illustrations anyway.

Nathan Smith
June 5th, 2007
12:00 PM | #

Ryan: This is one of my pet peeves of IE6 as well. I wrote about my findings and struggles with it awhile ago, regarding background images...

Article here:

http://sonspring.com/journal/ie6-multi-class-bug

Demo here:

http://host.sonspring.com/multi-class/

Jens Meiert
June 5th, 2007
12:55 PM | #

Ryan, I said "doesn't support", not "doesn't fully support". Also, you still seem to refer to the symptoms, not the cause. That's the only thing I wanted to point out. [...]

drew
June 5th, 2007
4:35 PM | #

The relevant rule here is found at:
http://www.w3.org/TR/CSS21/selector.html#class-html

When using this dotted notation, the selector is the entire chain not the last attribute. That is "box featured" or "featured box" is the same as div.box.featured; as it should be. It is not the same as applying two classes "box" and "featured". Indeed the way the css is written you don't have a class "featured". You have "box" and "box.featured".

It appears that both FF and IE6 apply box and then box.featured. IE6 does mistakenly apply nonexistent class="featured" by itself. FF correctly does not.

When I do your zebra table, I get the same result in IE6 and FF2.0.0.4. That is i get light, dark, light, white, light. Both browsers seem to be interpreting the rules correctly.

It's an obscure and somewhat difficult rule but that's what it is.

Carlos Eduardo
June 5th, 2007
4:58 PM | #

Very useful article.
I always use multiple CSS classes on elements and it works... But on my last work, I had some problems with IE6 and these multiple classes...

Thank you for explaining that... Now I know what happens when it doesn't works on IE6 :)

Ryan Brill
June 5th, 2007
5:43 PM | #

Drew - "Indeed the way the css is written you don't have a class 'featured'. You have 'box' and 'box.featured'."

Exactly. I don't want the class of .featured to be used alone, as my CSS is written to expect the module to also have the class of .box. However, IE6 would allow it to.

"When I do your zebra table, I get the same result in IE6 and FF2.0.0.4."

I had a problem in the code, which has been fixed. I forgot to add the td after tr.selected and tr.alt.selected. It has been updated, and you can now see the problem. I've also put together this demo where you can see the problem.

Geoff
June 6th, 2007
4:35 AM | #

Thanks for the post Ryan, its worth knowing.

I personally find dropping in a conditional only for ie6 the quickest, cleanest method to avoid such annoyances with the black sheep of the family (IE6)

It may not be to every-bodies taste but it does get the job done and is manageable for other editors of the source code.

Ryan Brill
June 6th, 2007
9:15 AM | #

Geoff - I don't think you can hack this particular issue with conditional comments. The issue isn't that it needs different CSS than other browsers, but rather that it doesn't understand the difference between the selectors.

drew
June 6th, 2007
11:37 AM | #

I'm glad you did the revised demo because it shows that this is about dotted notation not multiple classes.

You've written css which says: 1) for td descended from tr use a white background; 2) for td descended from tr with a class of "alt" use a background of blue1; 3) for td descended from tr with a class of "selected" use a background of blue2; 4)for td descended from tr with a class of "alt selected" use a background of blue3. You then create a table containing examples of each of these single classes.

IE6 gets this wrong because it doesn't understand that "alt selected" is a single class. It treats "alt" and "selected" as multiple classes instead of the single class it actually is. It therefore applies the already know "selected" rule to "alt selected" and gets it wrong. Move the "alt selected" rule to first in the list and watch the change to confirm this. That's simply wrong, as you point out. But it is wrong because IE6 doesn't understand the class declaration not because it can't manage multiple classes since there aren't any multiple classes here.

FF gets it right because it understand dotted notation and is able to distinguish between the classes "alt", "selected", and "alt selected". If it couldn't distinguish properly then writing "selected alt" would change the color since both classes deal with background and last written would apply. Instead "alt selected" and "selected alt" produce the same background color; only possible if both names are interpreted according to the spec rule.

The box and featured example hides this. Again you have two distinct classes: "box" and "box featured" (same as "featured box"). Following the logic then when you invoke "box featured" you should only get the background color not the border. Yet you do get the border. Confusing. And suggests FF is wrong.

There is a note in the spec which speaks to the difficulty of interpreting space-separated class attributes like those used here and says that therefore such behavior is explicitly non-normative. This follows a cautionary note about not over using class so as to overwhelm presentational dimensions of elements.

This suggests that the longer the chain in dotted notation the less reliable the implementation. As your examples taken together show, more than one class attribute tends to muck things up. Better to use descent and inheritance than to hope the browser guesses your intention.

Ryan Brill
June 6th, 2007
12:32 PM | #

Drew - "...it shows that this is about dotted notation not multiple classes."

The two are not mutually exclusive, as you seem to be implying. "Dotted notation" is the issue, but it is based on IE's misunderstanding of the chain of selectors (in CSS) in multiple classes.

"IE6 gets this wrong because it doesn't understand that 'alt selected' is a single class. It treats 'alt' and 'selected' as multiple classes instead of the single class it actually is."

Actually, "alt selected" is not a single class, but is indeed 2 separate classes. If you write your css as .alt {} and .selected {}, browsers will apply both classes, as they should. In our example, the .selected class will overrule the .alt class simply because of the cascade order. However, when we write our CSS as a subset class (chain of classes [.alt.selected]), it should only apply if both classes exist on the element. IE disregards this, and will apply the .alt.selected styles to an element as long as it has the .selected class.

"Following the logic then when you invoke 'box featured' you should only get the background color not the border. Yet you do get the border. Confusing. And suggests FF is wrong."

No, what should happen is exactly what does. You should get all the CSS associated with .box, and all the CSS associated with .box.featured should overwrite the styles set by .box (due to cascading order, again).

"There is a note in the spec..."

I believe you are misreading the note. I don't think it is warning against using multiple classes at all, but rather about warns against building an entire site using nothing but divisions, for instance, and applying classes to each one. For instead a division with the class of paragraph to replace the actual paragraph element.

drew
June 7th, 2007
8:35 AM | #

Last exchange I suspect since neither of us is convinced by the other's logic and you will get the last post as blog owner.

Since IE6 gets it wrong and is still the dominant browser, I'd rather use cascade, inheritance and non-chained multiple classes than the typical hacks which may cause additional problems in the future and/or resorting to browser specific style sheets. One style sheet across the maximum number of browsers.

With regard to the notes, I agree completely with your interpretation of the first note. However, there are two because there are two subjects otherwise the second should be a second paragraph of the first note not a separate note. Nor do I beleive that the second note speaks to overuse and inappropriate use of class in building a site:

"If an element has multiple class attributes, their values must be concatenated with spaces between the values before searching for the class. As of this time the working group is not aware of any manner in which this situation can be reached, however, so this behavior is explicitly non-normative in this specification."

The first sentence seems pretty clear: concatenate multiple classes then search for the class. The second isn't the cleanest prose I've ever seen but seems to say, we don't know how to do this properly so do the best you can. To me that says be very careful how you use chaining.

Adam Burmister
June 8th, 2007
8:04 PM | #

The effect of multiple class names can be achieved in IE6 by using careful ordering of your class definitions.

div.box { }
div.featured { }

So the box class cascades down to the featured class... in this case, the featured class can override properties defined within the box class.

Prestaul
June 11th, 2007
1:11 PM | #

Drew, you are reading the note entirely wrong:

"If an element has multiple class attributes, their values must be concatenated with spaces between the values before searching for the class."

An element (read: tag) with multiple class attributes:

<a class="foo" class="bar" href="#">foo bar</a>

This is an element with multiple class attributes. The class attributes are to be treated the same as an element with the two classes concatenated with a SPACE between them (not a dot):

<a class="foo bar" href="#">foo bar</a>

Ryan is dead on. Of course IE will apply multiple classes:

a.foo { border: 1px solid #000 } AND a.bar { background: #666 }

But it will not recognize css SELECTORS with multiple classes:

a.foo.bar (border: 1px solid #000; background: #666 }

Is anyone aware of a work around?

drew
June 12th, 2007
12:38 PM | #

Prestaul wrote:

Drew, you are reading the note entirely wrong:

"If an element has multiple class attributes, their values must be concatenated with spaces between the values before searching for the class."
----------------------
If that were the entire note you might be correct but there is a second line which needs to be considered.

Consider the example used in the specs:

p.marine.pastoral{color:green;}

Matches for class="pastoral blue aqua marine" but does not match for class="pastoral blue". Take it a step further. Assign the obvious colors to blue and aqua. What color will apply to text in p?

Marine.pastoral yields green but what happens to blue and aqua? What rules does the browser follow to decide? If the rules are read serially, it will be "nothing, blue, aqua, nothing" since marine and pastoral aren't separately defined. Perhaps "green blue aqua" Or "blue aqua green"?

This is why the second sentence is in the note. As an example this is contrived for humans but perfectly sound for browsers. The situation that can't "be reached" is how to interpret the space separated concatenation. Hence the note about non-normative status for dotted notation of multiple classes. Far better to rely non-chained, selectively order, space separated multiple classes.

So p.green, p.blue, p.aqua (each selector written with the obvious color) as "class= green blue aqua" would be serially applied and text would be the last color.

IE6 gets it wrong but it's immaterial since you can't get it right.

Prestaul
June 13th, 2007
5:49 PM | #

Drew wrote:

"Marine.pastoral yields green but what happens to blue and aqua? What rules does the browser follow to decide?"

Drew, this is very clearly defined in the specification and is based upon the specificity of the rule first and then the order in which the rules occur. (http://www.w3.org/TR/CSS21/cascade.html#specificity)

But that is a different discussion...

About the rest of your post: You are simply misreading the note. What is an element? What is an attribute? What would multiple class attributes on an element look like? Search for xml attribute or xhtml attribute and also for xml/xhtml element and figure out exactly what that note is about. It is not about multiple/chained classes in a css selector at all so the basis of your post is flawed.

The condition that cannot be reached is two class attributes on an element. (i.e. <a class="red" class="blue">) Simply because the specifications for HTML/XML/XHTML/SVG/MathML/WhateverML don't allow for multiple class attributes. The css spec is does not care what type of markup is referencing it so there is not saying that some markup may not include multiple class attributes on a single element... Am I off base?

Prakash Bhatt
June 15th, 2007
2:30 AM | #

Hey I really like this but rather than write like <tr class="alt selected">
one can always define a new class as alternateSelected and write it like <tr class="alternateSelected">.
So My question is - Is there any difference between both type?
I am new to css and also not aware of any programming language except a little JS.

can you reply me back on bhattprakash@gmail.com
thanks

This comment was edited by Prakash Bhatt at 02:35 am

Prestaul
June 15th, 2007
3:08 PM | #

Sure. If you are defining static HTML you can easily build composite classes:

.alt { background: #eee }
.selected { background: #bdf }
.alt_selected { background: #9bd }

However, if you are using javascript to select rows without reloading the page you don't want to have to look to see if the row has the "alt" class to decide if you need to add "alt_selected" and remove "alt" or just add "selected", you just want to add "selected" to select the row and remove it to unselect. The same applies when you generate the HTML from a server-side script (like php, asp, etc.). You save (and simplify) code by saying if selected add "selected", if alternate then add "alt" rather then needing the third case where both apply.

Using two classes is also simply more pleasing. What type of row is it? It is a selected row and it is an alternate row. Why do I need to define a new class for something that is both?

Jasja
June 19th, 2007
3:54 PM | #

Interesting article, when designing you automatically run into different issues concerning IE6 some more easy to fix then other, so thanx for pinpointing this issue.

Mark B
June 27th, 2007
8:24 AM | #

IE 6 does support "Chained" classes.

For instance:

div {
color: green;
margin: 10px;
background-color:maroon
}

div.border {
border: solid 1px black;
}
div.background {
background-color: black;
}

div.background.border {
background-color: yellow;
}


will display as you expect (divs will be maroon, unless they have a "background" class, and then they will be black, unless they also have a border class, in which case they will be yellow)

HOWEVER, I have noticed that in some cases where it's a little ambigous which rule should be used, it can be ambiguous.

For instance, in IE 6,
body div.background
wins over
div.background.border
(but body div.background.border wins over both)

This is different from firefox and IE7.

Although
div.background
wins over
body div


Also, I've noticed that IE6 is sensitive to order of CSS when you start using multiple classes.

for instance
if I put the div.background rule first followed by the div.background.border rule, I'm fine. but if I put div.background second, it always wins.


The reason for this is probably that IE6 treats all classes as "1" point, when calculating specificty. (it matches correctly, but

Anyhoo, the net result is, IE6 generally supports everything you need for multiple classes, you just have to be very careful when writing you CSS to put the most specific stuff at the end, and avoid having a rule that is more specific than any other rule on the basis of having multiple classes, when it's got few element points.

http://www.w3.org/TR/CSS21/cascade.html#specificity

Prestaul
June 27th, 2007
12:54 PM | #

Mark - IE6 does NOT support chained class selectors. Your example was flawed because your classes specified style changes that are exclusive. (i.e. background changed background color and border changed border style, but neither effected the other)

If you chain classes in a selector IE6 ONLY RECOGNIZES THE LAST ONE. This means that in IE6 this:

div {
color: green;
margin: 10px;
background-color:maroon
}

div.border {
border: solid 1px black;
}
div.background {
background-color: black;
}

div.background.border {
background-color: yellow;
}

is equivalent to:

div {
color: green;
margin: 10px;
background-color:maroon
}

div.border {
border: solid 1px black;
}
div.background {
background-color: black;
}

div.border {
background-color: yellow;
}

Test it and see if I'm right on that.

Or, try this:

div.background {
background-color: black;
}

div.border.background {
background-color: yellow;
}

and see if you can get a div with class="background" to show up with a black background in IE6... You can't.

Victor
July 11th, 2007
9:52 AM | #

Oww that pretty neat. pretty useful for on the fly class changes and some nice ajax effects can be done with this

Comments are automatically closed after 45 days