Reputation: 6747
I want to combine a class selector like .smart
with a [attr^=val]
selector, whose attr
is a class
, like [class^='test-']
. I have tested each one of the selectors individually and they seem to work just fine. However, when combined, they fail to produce the desired result.
You can also view the Codepen.
/* Works */
[class^='test-'] {
background: blue;
}
/* Works */
.smart {
background: yellow;
}
/* Doesn't work */
[class^='test-'].smart {
background: red;
}
<div class="test-me">
<p>Should be blue.</p>
</div>
<div class="smart">
<p>Should be yellow.</p>
</div>
<div class="smart test-me">
<p>Should have been red, but isn't.</p>
</div>
Can anyone explain why the CSS selector [class^='test-'].smart
does not work and, if possible, how to fix the problem?
Upvotes: 3
Views: 701
Reputation:
This is not a direct answer to your specific question. Other answers are fine.
However, I would not recommend this approach of giving classes names with internal structure, which you then have to pick apart using the attribute selectors. Attribute selectors provide you with ability to find any attribute whose value starts with a string (^=
), contains a string (*=
), contains a token somewhere (~=
), or whose value is a prefixed string (^=
), but not to find an attribute whose a value is a list of tokens any of which has a particular prefix, which is what you want. Therefore, as explained in other answers, you are forced to say
[class^="test-"], [class*=" test-"]
That's ugly and not very dry. In addition, in case you are worried about performance, it's going to be a lot more work for the CSS engine. In contrast, CSS engines are highly optimized to do regular class matching.
Therefore, I would recommend that you adopt the approach of separate classes, test
and me
. The HTML would obviously look like <div class="test me">
. The CSS would specify .test { }
for all properties common to all flavors of test, and .test.me { }
for properties specific to me
.
If you are worried that me
might conflict with a similarly-named class somewhere else in your code, then you could "namespace" it as "test-me", and specify the HTML as <div class="test test-me">
, and the CSS as .test { }
and .test-me { }
.
Yes, this requires a few more bytes in your HTML. But the cost of downloading those after zippping and caching even for a million users over the course of a year is likely to be absolutely negligible.
But the fact remains that test test-me
is a little bit more wordy than just test-me
in your HTML. However, I would make the case that the former is ultimately more readable and semantic. Consider the poor guy who takes over your code a year from now. If he wants to find what styles are being applied to test-me
, he is likely to search the CSS for classes of the form .test-me
. He may not stumble across the other rule [class^="test-"], [class*=" test-"]
which is affecting this class="test-me"
element.
If you look at Bootstrap, notice that they do not use attribute selectors for classes such as col-md-3
. That would be far too inefficient. Instead, the rules applied at run time explicitly name all the possibilities:
.col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9 {
float: left;
}
Of course, in the case of Bootstrap, they do not write out all those classes in the source code; they use SASS to generate them at compile time. That is also an option you could consider, if you're willing to use SASS or some other pre-processor, or already do so.
Another useful reference point is icon libraries which allow you to specify icons with something like
<i class="icon-cloud"></i>
Some of them do use CSS rules of the type you are proposing, but they assume that there will be only one class specified, and so they can get away with just a single rule
[class^="icon-"]
which is going to be at least not quite so bad performance wise. However, other icon libraries make you explicitly specify another class just indicating icon-ness by itself:
<i class="icon icon-cloud"></i>
This allows them to write their CSS for properties common to all icons simply as
.icon { font-family: MyCoolIconFont; }
which will be as fast as it could possibly get.
Upvotes: 0
Reputation: 1696
You need to change your CSS in this format:-
Just need to change ^ by *
[class*='test-'].smart {
background: red;
}
Upvotes: 0
Reputation: 723438
why the CSS selector
[class^='test-'].smart
does not work
Because your class attribute doesn't start with test-
. It starts with smart
. If it did start with test-
, like so:
<div class="test-me smart">
<p>Should be red.</p>
</div>
then it'd match.
how to fix the problem
You need an additional selector for when the attribute doesn't start with test-
. As described here:
[class^='test-'].smart, [class*=' test-'].smart {
background: red;
}
<div class="smart test-me">
<p>Should be red.</p>
</div>
<div class="test-me smart">
<p>Should also be red.</p>
</div>
Upvotes: 7