Styling sites that have few or no IDs or classes
One of the frequently asked questions I get is how to make styles for sites that have few or no IDs or classes. To explain, I’ll have to go over some CSS concepts. It helps to think of CSS elements as a family (it’s also the way the spec was written), so start thinking of an element beside another as a “sibling”, an element inside another as a “child”, etc.
To select portions of a document with CSS, you make use of the aptly named “selectors”. Common selectors are the ID selector (#id), the class selector (.class), and the element selector (table). These selectors can be combined, for example a.class matches all a elements with a class of “class”.
Another type of selector is the combinator. Combinators let combine other selectors to represent a relationship between elements. The common combinator is the descendant selector. For example, a img selects all img elements that have an ancestor (are inside) of an a element.
CSS offers many more selectors which are less-known because they’re newer, not as well supported, or not as useful in the context of writing webpages (where you can plunk an ID or a class wherever you want).
- The attribute selectors allow you to select elements with an attribute with the name/value you specify.
img[alt]matches all images with an alt attribute, no matter what the value (even no value or an empty string).img[alt="Some useful alt text"]matches all images with that exact alt value.img[alt*="text"]matches all images with an alt attribute that have the word “text” anywhere in them. Similarly, you can replace the * with ^ for “starts with” and with $ for “ends with”. - Similar to the descendant selector mentioned earlier, the child selector is a combinator selects all elements that are direct children (directly inside) of another element. For example, given the HTML code
<h2><a><img>Text</a></h2>,h2 > amatches the a,a > imgmatches the img, buth2 > imgdoes not match the image because it’s not a direct child. - Sibling selectors are another kind of combinator. They let you select an element depending on what its older siblings are. (”Older” = happens earlier in the document, “sibling” = has the same parent). There are two types of sibling selectors, direct (+) and indirect (~). Direct means that the second element must be immediately after the first, indirect means that it only has to be somewhere after the first. Given the HTML code
<a>Text</a><img><span>More text</span>,a ~ spanmatches the span, buta + spandoesn’t because the span isn’t immediately after the a. - Structural psuedo-classes are like combinators in that they allow you to specify an element by position, but they give you meta-data about the element.
a:first-childmatches all a elements that are the oldest (first) child of their parent. Along the same lines, there are last-child and only-child pseudo-classes. - The negation pseudo-classes lets you negate other selectors.
*:not(h1)matches everything except h1 elements.img:not([alt])matches every img element without an alt attribute.a:not(:first-child)matches all a elements that aren’t first children.
To select an element on a page with few or no IDs or classes, you need to make use of these selectors. One method is to be incredibly specific with attribute selectors. Given the HTML code <table cellpadding=0 cellspacing=1 border=1>, you could select it with table[cellpadding="0"][cellspacing="1"][border="1"]. You could also use the negation pseudo-class in this case. What you have to be careful about with this method is that there isn’t some other table with that specific combination.
Another method is to use the combinators and structural pseudo-classes to walk down the tree to the desired element. For example, body > table > tr:first-child + tr + tr > td would match the cell of a third row of a table that’s directly inside the body element. You don’t necessarily need to start with the body element, you could start on an element that does have an ID or class, or you could use an “incredibly specific attribute selector” to get a starting point.
Unfortunately, the problem with these methods is that they’re fragile. If the site changes their code from cellspacing=1 to cellspacing=2, or if they add an extra div, the selectors may no longer apply.
For more information, see the CSS3 Selectors Candidate Recommendation. That document covers all selectors in CSS3, not just the new ones, but be warned, not all selectors are currently supported by any browser.
October 17th, 2006 at 11:27 am
There’s an error. h2 > img does not match the image in that code, it isn’t a direct child, but a > img does.
October 17th, 2006 at 12:14 pm
Looks like I changed the example without changing the selectors (I was wrestling with Wordpress over the use of HTML). It’s fixed now, thanks.
October 22nd, 2006 at 1:01 pm
Great post Jason, you should consider adding this content linked to http://userstyles.org/style/tips.
In a future blog post, would you consider explaining your take on the use of whitespace, it’s obviously great for readability, but what is the real world performance impact?