Understanding CSS Selectors

Emily P. Lewis | February 8, 2011

 

I recently came across Enterprise CSS, and chuckled when this gem appeared on the screen:

As I have a keen sense of humor, I continued on to Enterprise HTML, where the hits kept coming:

Now that I’m freelance, I can laugh at these. But, once upon a time, I worked for a large organization that regularly outsourced large-scale web development to agencies that “specialized” in enterprise solutions. And, all too often, when the HTML and CSS landed on my desk, it was in exactly this sort of state. Unnecessary classes and ids, inefficient selectors and jumbled markup became my daily nemeses.

I’ll never forget how much of my (and, subsequently, my employer’s) time was wasted trying to work with our “enterprise” CSS.  It really isn’t all that funny when you have to deal with it. It’s even more sobering when you consider the time, money and resources wasted due to inefficient CSS.

So, why does it happen?

Part of me blames the corporate world; the “enterprise” solution world. This world frequently defines cross-browser support as “looks the same in every browser, including IE6.” We developers who work in this world rarely have the option of using advanced CSS3 selectors. We can’t even use some of the not-so-advanced CSS 2.1 selectors.

But overzealous CSS and class-itis aren’t restricted to that corporate world.  You can find them everywhere, from small business to government sites. Lack of knowledge and experience must play a role.

I know from experience how rare it is to have time to experiment with new approaches or to stay up-to-date on which browsers support what. As such, doing what has always worked is often a safe bet.

I can’t do much about the corporate mentality (that’s why I went freelance). I can, though, share a bit of knowledge and, hopefully, pique your curiosity enough so that you will experiment as I have.

Attribute Selectors

CSS3 seems to get all the attention lately. It’s understandable. Us designers tend to get googly-eyed when we hear “rounded corners” and “multiple background images.” But CSS 2.1 offers one of my favorite selectors, which I’ve been using like it is going out of style (yes, the pun is intended).

Attribute selectors allow you to target elements according to their attributes, and are supported by all the latest browsers:

With attribute selectors you have several ways to target elements via their attributes:

[attr] selects based strictly on attribute:

img[alt] {
    border: 1px solid #000;
    padding: 7px;
}

This example targets all images with an alt attribute:

<img src="EmilyLewis.jpg" alt="Emily Lewis" />

[attr=value] selects based on an exact attribute value:

a[rel="home"] {
    background: url(home.png) no-repeat 0 0;
    padding: 0 0 0 20px;
}

This example targets all links assigned rel="home", which is handy if you are taking advantage of the rel-home microformat:

<a href="http://emilylewisdesign.com" rel="home">Home</a>

Also note the syntax for specifying an attribute value. When indicating a string, as in the above example, the value must be contained by double quotes:

a[rel="home"]

[attr~=value] selects based on an attribute that contains a series of whitespace-separated words and one of the words is the exact value specified in the selector:

img[alt~="website"] {
    border: 1px solid #000;
    padding: 7px;
}

This example targets all images with an alt value containing the word “website,” which can be useful if you follow standard alt text conventions for certain types of images:

<img src="ClientXYZ_Thumb.jpg" alt="Client XYZ website thumbnail" />

[attr|=value] selects based on an attribute value that starts with the exact value specified in the selector and is appended with a hyphen:

img[src|="portrait"] {
    border: 1px solid #000;
    padding: 7px;
}

This examples targets all images with a src value that begins with “portrait-”, which is useful if you follow standard naming conventions for images:

<img src="portrait-EmilyLewis.jpg" alt="Emily Lewis" />

With CSS3, we get three more attribute selectors to work with (which share the same browser support as the above 2.1 selectors):

[attr^=value] selects based on an attribute value that starts with the exact value specified in the selector:

a[rel^="met"] {
    background: url(XFN_met.png) no-repeat 0 0;
    padding: 0 0 0 20px;
}

This example targets all links with a rel value that starts with “met,” which happens to be handy if you are a fellow fan of the XFN microformat:

<a href="http://emilylewisdesign.com" rel="met friend colleague">Emily Lewis</a>

[attr$=value] selects based on an attribute value that ends with the exact value specified in the selector:

img[alt$="portrait"] {
    border: 1px solid #000;
    padding: 7px;
}

This example targets all images with an alt attribute that ends with “portrait”:

<img src="EmilyLewis.jpg" alt="Emily Lewis portrait" />

[attr*=value] selects based on an attribute that contains the value specified in the selector:

img[alt*="landscape"] {
    border:1px solid #000;
    padding: 7px;
}

This example targets all images with alt values containing the word “landscape,” regardless of whether it appears in a whitespace-separated list:

<img src="Albuquerque_Landscape.jpg" alt="Albuquerque, New Mexico landscape, circa 1901" />

Improved Efficiency

With just those examples listed above, I’ve demonstrated several ways to target elements without relying on the addition of any classes or ids. Instead, I can rely on conventions I already follow:

  • XFN for my “human network” links
  • rel-home for home links, not to mention other rel-based microformats for links
  • Prefix naming conventions for image files, such as “portrait-”
  • Alternative text conventions, such as always including the word “thumbnail” for those types of images

Don’t misunderstand me. I use and recommend classes and ids, particularly when you need greater specificity. But when working in that “enterprise” world of huge sites with hundreds of HTML templates and dozens of CSS files, eliminating the need to add classes or ids can save hours during both creation and maintenance. And even now that I’m no longer in that environment, I still appreciate the little efficiencies that help me get my work done faster.

All that said, there is another sort of efficiency worth mentioning: how browsers render CSS. Attribute selectors are not as efficient as single ID selectors in terms of browser rendering.

Is this a huge deal compared to the maintenance nightmare of CSS comprised entirely of ID selectors? For me, it’s something to consider but not a deal-breaker. Plus, browsers render ID selectors less efficiently when used as descendent selectors or combined with tag and class selectors … approaches I frequently saw in “enterprise” CSS.

ARIA Roles

I have to admit that it isn’t just the added efficiency that gets me ga-ga over attribute selectors. I like that they happen to go perfectly with my latest interest in implementing ARIA roles. For the past few sites I’ve worked on, I’ve added ARIA document landmark roles because I’m eager to build for accessibility, even if it is just potential accessibility.

For example, the markup for primary navigation can include role="navigation":

<ul role="navigation">
    <li><a href="/" rel="home">Home</a></li>
    <li><a href="/About/">About</a></li>
    ...
</ul>

And to target this markup for styling, I simply use:

ul[role="navigation"] {
    some pretty styles here;
}

I can’t help but get geekily excited about taking advantage of “advanced” CSS selectors plus forward-thinking ARIA.

Forms

I’ve also become enamored with using attribute selectors for styling forms. Again, rather than relying on classes or ids to target form elements, I can utilize the non-presentational attributes already needed for functionality.

For example, to target a submit button:

<input type="submit" name="searchSubmit" value="Submit" />

I’d use an attribute selector:

input[type="submit"] {
    pretty styles here;
}

Or if I wanted to style labels differently:

label[for="comment"] {
    more pretty styles;
}

Worth Noting

Each of the examples I’ve provided relies on a single attribute selector. However, you can use multiple selectors for greater specificity:

input[type="submit"][value="Search"] {
    now some sexy styles;
}

This example would only target submit buttons with a “Search” value:

<input type="submit" name="searchSubmit" value="Search" />

While other submit buttons wouldn’t receive the styles:

<input type="submit" name="emailSubmit" value="Submit" />

Also, case sensitivity has an inconsistent history. Earlier versions of Safari and Firefox both ignored casing, even for attributes that should be case sensitive. Other browsers, meanwhile, haven’t had issues adhering to case sensitivity.

The takeaway? Stay on the safe side and make sure you match the casing in your markup with that in your CSS.

No IE6, No Can Do?

Now, if you are one of those folks constrained by IE6 requirements, much of what I’ve detailed in this article isn’t going to be something you can rely on entirely. But that doesn’t mean you can’t experiment and continue to support your IE6 audience.

If you take an approach of graceful degradation, you could use attribute selectors for styles that won’t adversely affect the IE6 audience if the browser doesn’t render them. Consider the example where I targeted all links assigned rel="home":

a[rel="home"] {
    background: url(home.png) no-repeat 0 0;
    padding: 0 0 0 20px;
}

This simply adds a little icon to the left of the link, providing a small visual indicator that it is a link to the site’s home page.

IE6 doesn’t recognize the selector, so the icon doesn’t display. However, the site itself, as well as the user experience is, arguably, unaffected … A good scenario where you can experiment, but still ensure IE6 support.

If you prefer the progressive enhancement side of the coin and can rely on JavaScript, consider tools like Selectivzr to emulate attribute selectors in IE6.

A Deep Pool

There are a lot of CSS selectors. And there are even more articles providing a high-level summary of those selectors. Many of these are pretty good resources, which you should check out to see what else is available to you:

Be Prepared

Even after 12 years of writing CSS, I still encounter bugs. I still make mistakes. I still don’t have the full spectrum of browser support memorized. What I do have are some really solid resources that I turn to each and every time.

For the fundamentals of selectors, specificity, the cascade and probably more than you ever thought you’d want to know, check out the tried-and-true Selectutorial. To figure out browser support, I’ve found nothing more useful than QuirksMode’s CSS contents and browser compatibility chart. And it couldn’t hurt to consider some best practices for CSS development.

Happy styling!

 

About the Author

Emily Lewis is a freelance web designer of the standardista variety, which means she gets geeky about things like semantic markup andCSS, usability and accessibility. As part of her ongoing quest to spread the good word about standards, she writes about web design on her blog, A Blog Not Limited, and is the author of Microformats Made Simple and a contributing author for the HTML5 Cookbook. She’s also a guest writer for Web Standards Sherpa.net magazine and MIX Online.

In addition to loving all things web, Emily is passionate about community building and knowledge sharing. She co-founded and co-manages Webuquerque, the New Mexico Adobe User Group for Web Professionals, and is a co-host of the The ExpressionEngine Podcast. Emily also speaks at conferences and events all over the country, including SXSW, MIX, In Control, Voices That Matter, New Mexico Technology Council, InterLab and the University of New Mexico.

Find Emily on: