This article explains how to keep your markup slim and target elements in the DOM without resorting to extra presentational markup or JavaScript by using CSS3 selectors.
To style an element with CSS, we need to be able to target it. Enter CSS selectors, which enable us to target elements at a more granular level without adding presentational markup or attributes.This means your markup can be super slim, semantic and flexible. There isn’t space to cover all the CSS3 selectors here so we’ll concentrate on attribute and substring selectors followed by structural and negation pseudo-classes.
With a range of selectors available, it can be difficult to know which type of selector to use. As the new CSS3 selectors are introduced, we’ll provide real-world use cases for them to use in your projects right away.
Attribute and substring selectors
CSS3 has extended the list of attribute selectors we can use by adding three new substring selectors to our toolkit. This means we can now target a rule and apply CSS styling to an element based on part of an attribute’s value. We’ll briefly describe each below.“Starts with” substring selector
The caret (^) operator, when related to the substring selector, means “starts with”. For example, this could be used to target all external links in our content by adding a small icon indicating an external link. In the following example, we’ll add a background image and padding to all links that start with http:// by using the ^ “starts with” attribute substring selector as below:- a[href^="http://"] {
- background:url(img/external.png) 100% 50% no-repeat;
- padding-right:15px;
- }
- a[href^="http://"] {
- background:url(img/external.png) 100% 50% no-repeat;
- padding-right:15px;
- }
- a[href^="http://mysite.com"], a[href^="http://www.mysite.com"] {
- background:none;
- padding-right:0;
- }
mailto: string and add an icon such as a mail icon:
- a[href^="mailto:"] {
- background:url(img/email.png) 100% 50% no-repeat;
- padding-right:15px;
- }
“Ends with” substring selector
We’ve dealt with “starts with”, now we’ll look at “ends with”. The operator for this is the dollar symbol, $.The syntax is exactly the same as the “starts with” syntax and common use cases include adding icons to represent different files types for document downloads or to indicate different feed types. To indicate that a link goes to a PDF document, we can use:
- a[href$=".pdf"] {
- background:url(img/pdf.png) 100% 50% no-repeat;
- padding-right:18px;
- }
"Contains" substring selector
The final substring selector uses the star or asterisk (*) operator, which stands for “contains”. This is powerful when we wish to target elements that have more than one class applied. It can also be used to target specific domains within an anchor. We’ll use it in the following example to highlight those anchors linking to a person’s Twitter account:- a[href*="twitter"] {
- background:url(img/twitter.png) 100% 50% no-repeat;
- padding-right:20px;
- }
- a[href*="chrisdavidmills"] {
- background:url(img/twitter.png) 100% 50% no-repeat;
- padding-right:20px;
- color:#ff0;
- }
- <a href="http://twitter.com/chrisdavidmills" rel="met friend">Chris Mills</a>
- a[rel*="friend"] {
- background:url(img/friend.png) 100% 50% no-repeat;
- padding-right:20px;
- }
- a[rel*="friend"] {
- background:
- url(img/friend.png) 100% 50% no-repeat,
- url(img/twitter.png) 85% 50% no-repeat;
- padding-right:40px;
- }
Structural pseudo-classes
Structural pseudo-classes let us style elements and parts of elements that are in the DOM but can’t be targeted with other selectors. They keep your markup clean and efficient, replacing the addition of extraneous classes in your markup, either those in the source code or those added dynamically using JavaScript.:last-child
This is for targeting the last child of another element. We can use it to remove the border from the last item in a list, for example:- nav li:last-child {
- border-right:0;
- }
way as :last-child except it targets the first matched child of another element.
:nth-child
The :nth-child pseudo-class lets us target one or more specific children of a given parent element. It can take the form of a number (integer), keywords (odd or even) or a calculation (expression). It can come in handy when you want to style data tables or complex lists (see issue 216, page 78). We’ll start by showing the keyword value even to add a background colour to create a zebra-striping effect on alternate table rows to improve readability that might traditionally be achieved server-side, using JavaScript or by adding classes to your markup:- tr:nth-child(even) td {
- background-color:#eee;
- }
- tr:nth-child(2n) td {
- background-color:#eee;
- }
- tr:nth-child(odd) td {
- background-color:#eee;
- }
- tr:nth-child(2n+1) td {
- background-color:#eee;
- }
- tr:nth-child(4n) td {
- background-color:#eee;
- }
- tr:nth-child(4n+2) td {
- background-color:#eee;
- }
- tr:nth-child(-n+5) td {
- background-color:#eee;
- }
:nth-last-child
The :nth-last-child pseudo-class is essentially the same as :nth-child but it starts counting from the last element. Using the same expression as the previous example we can highlight the last five rows in the table:- tr:nth-last-child(-n+5) td {
- background-color:#eee;
- }
:only-child
We have one more ‘child’ pseudo-class to look at. :only-child works by targeting any elements when they are the only child of its parent. This might come in useful if we have a dynamically generated list that only contains only one item, in which case the margins should be decreased:- ul li:only-child {
- margin-bottom:2em;
- }
:first-of-type
The ‘type’ pseudo-classes tend to work in the same way as the ‘child’ classes, the key difference being that the ‘type’ pseudo-class only targets those elements that are the same as the element the selector is applied to. They’re useful when we can’t guarantee there won’t be any other child elements in place. Eg, if an hr is placed between each paragraph, by using :first-of-type, we can ensure we target only the paragraphs. Consider an intro paragraph contained within a section (<section id="introduction">). Using :first-of-type, we can style the first paragraph within the section, like so:- #introduction p:first-of-type {
- font-size:18px;
- font-weight:bold;
- }
- #introduction p:first-of-type::first-letter {
- font-size:60px;
- float:left; width:auto;
- height:50px;
- line-height:1;
- margin-right:5px;
- }
:last-of-type
Using :last-of-type, we can achieve the same effect as :last-child. To remove the right-hand border from the last menu item, we can use:- nav li:last-of-type {
- border-right:0;
- }
:nth-of-type
:nth-of-type works in the same way as :nth-child and uses the samesyntax. However, it can be more useful than :nth-child should there be elements in between those being targeted. The following example has a section with a heading, followed by a list containing images of animals in space:
- <section id="animals">
- <h1>Animals in Space</h1>
- <ul>
- <li><img src="img/fly.png" alt="Fruit Flies" /></li>
- <li><img src="img/albert.png" alt="Albert II" /></li>
- <li><img src="img/mouse.png" alt="Mouse" /></li>
- <li><img src="img/tsygan.png" alt="Tsygan" /></li> <li><img src="img/laika.png" alt="Laika" /></li>
- [...]
- </ul>
- </section>
- #animals ul {
- list-style-type:none;
- width:670px;
- }
- #animals li {
- float:left;
- width:200px;
- text-align:center;
- margin-right:35px;
- margin-bottom:35px;
- }
- #animals li:nth-of-type(3n) {
- margin-right:0;
- }
:nth-last-of-type
Using :nth-last-of-type(1) {...} is the same as using :last-of-type but, combined with expressions, it lets us count backwards starting from the last item, like :nth-last-child. Using our nth-of-type example, we’ll move the last lonely animal from our list of 10 to the centre by adding a large left margin:- #animals li:nth-last-of-type(1) {
- margin-left:235px;
- }
:only-of-type
:only-of-type targets elements whose parent elements have no other children of the same type. Imagine we have an article that can contain several images but if only one image is included, we may want it to be full width. This is where :only-of-type comes into its own:- article img:only-of-type {
- width:100%;
- }
:empty
:empty can be an extremely useful pseudo-class. It represents an element with no content. Assume we’ve got a dynamically generated aside in our page: we use :empty to hide it if it has no content.- aside:empty {
- display:none;
- }
:not()
In many ways the negation pseudo-class :not() works in reverse to other selectors because it enables us to target elements that don’t match the selector’s argument. Strange, we know, but it’s very practical. A prime example would to be style all form inputs that are not submit buttons:- input:not([type="submit"]) {
- width:250px;
- border:1px solid #333;
- }
We can also use the negation pseudo-class during testing to catch those things that validation won’t. For example, let’s say we want to see all abbreviations that don’t have a title attribute specified. Just use:
- abbr:not([title]) {
- outline:2px dotted red;
- }
img:not([alt]) { outline:2px dotted red; }This is a technique Eric Meyer uses in his diagnostic CSS (meyerweb.com/eric/ tools/css/diagnostics/). Add the diagnostic file when testing to catch all these errors, fix them and remove the file when you’re ready to deploy to the site.
Browser support
So how does this all work in browsers in the real world? Well, CSS3 selectors are fully supported in IE9+, Firefox 3.5+, Chrome 4+, Safari 4+, and Opera 10+ (with three minor exceptions). Support in IE6, IE7 & and IE8 is virtually non-existent (IE7 & and IE8 support the general sibling combinator and all the attribute selectors), but we can get around this by polyfilling with native JavaScript or a jQuery library.One useful polyfill we can use is Selectivzr by Keith Clark. Alternatively, if we decide that some of these sprinkles are only added as enhancements and aren’t crucial for the site’s functionality, then it’s fine for them not to show in less capable browsers. The choice is yours.
One caveat with IE before v9 is that, when it’s grouping selectors and comes across a selector it doesn’t understand, it ignores the whole rule. So if we have:
- ul li:nth-child(3n), ul li.last {
- margin-right: 0;
- }
- ul li:nth-child(3n) {
- margin-right: 0;
- }
- ul li.last {
- margin-right: 0;
- }
Summary
We’ve learned that by using powerful CSS3 selectors, we don’t need to add unnecessary classes and IDs to our markup, ensuring we can truly separate our content and presentation from one another. We’ve seen how to target the first, last, odd or even items in a group. We’ve also learned how to target groups of elements using expressions and those on their own.We’ve seen how to use negative pseudo-classes to help with testing and diagnostics. But there are many more CSS3 selectors we haven’t had time to cover. These include UI element states pseudo-classes, the general sibling combinator, :target (which deserves an article of its own), the new double-colon syntax of ::before and ::after and many more.
Check out the Selectors Level 3 W3C Proposed Recommendation, which can be found at www.w3.org/TR/css3-selectors.
No comments:
Post a Comment