CSS3 gives incredible power for selecting elements within a page. You may not think this sounds very glitzy but trust me, it will make your life easier and you'll love CSS3 for it! I'd better qualify that bold claim…
You've perhaps used existing CSS attribute selectors to target rules. For example, consider the following rule:
img[alt] { border: 3px dashed #e15f5f; }
This would target any image tags in the markup which have an alt
attribute:
<img class="oscarMain" src="img/oscar.png" alt="atwi_oscar" />
You can also narrow things down by specifying what the attribute value is. For example, consider the following rule:
img[alt="atwi_oscar"] { border: 3px dashed #e15f5f; }
This would only target images which have an alt
attribute of atwi_oscar
. So far, so big deal we could do that in CSS2. What is CSS3 bringing to the party? Principally, three new "substring matching" attribute selectors…
CSS3 lets us select elements based upon the substring of their attribute selector. That sounds complicated. It isn't! We can now select an element, based on the contents of the attribute. The three options are whether the attribute is:
Let's see what they look like.
The "beginning with" substring matching attribute selector has the following syntax:
Element[attribute^="value"]
In practical use, if I want to select all images on the site that had an alt
attribute that began with
film
, I would write the following rule:
img[alt^="film"] { border: 3px dashed #e15f5f; }
The key character in all this is the ^
symbol which means "begins with".
The "contains an instance of" substring matching attribute selector has the following syntax:
Element[attribute*="value"]
In practical use, if I want to select all images on the site that had an alt attribute that contained
film
I would write the following rule:
img[alt*="film"] { border: 3px dashed #e15f5f; }
The key character in all this is the *
symbol which means "contains".
The " ends with " substring matching attribute selector has the following syntax:
Element[attribute$="value"]
In practical use, if I want to select all images on the site that had an alt attribute that ended with
film
I would write the following rule:
img[alt$="film"] { border: 3px dashed #e15f5f; }
The key character in all this is the $
symbol which means "ends with".
How can these substring attribute selectors actually help? Let me give you an example where I often use CSS3 attribute selectors. If I build a website with a Content Management System (for example, Wordpress, Concrete, or Magento), it often gives the client the ability to add new pages. For example, perhaps they are adding a piece of news about their company or a product update. Each time they add a page in the CMS, the generated HTML will include an ID value for the <body>
or other relevant tag, which helps distinguish the page, markup wise, from others. For example, one client was involved in Motorsport and had a "Racing History" section with yearly reports. Each <body>
tag would have an ID for the year:
<body id="2003">
IDs can start with numbers in HTML5
If you're not used to coding in HTML5, you might assume that an ID beginning with a number is invalid, as it was in HTML 4.01. However, HTML5 removes that restriction, the only things to remember with ID names in HTML5 is that there should be no spaces in the ID name and it must be unique on the page. For more information visit http://dev.w3.org/html5/spec/Overview.html#the-id-attribute.
I needed the navigation bar link for "Racing History" to be highlighted when any of these yearly pages were viewed, as they related to the "Racing History" section. However, rather than write a style rule covering every future year, I was able to write a defensive (they are sometimes referred to as "defensive" rules as they try and safeguard against future events) CSS3 rule:
body[id^="2"] .navHistory { color: #00b4ff; }
This means that any element with a class of .navHistory
, that is a descendant of a body with an ID beginning with 2
(for example, 2002, 2003, 2004, and on) will be colored with the hex value of #00b4ff
. One simple rule covers all eventualities. Unless of course the website is still in its current form by the year 3000—in which case, chances are, even if I eat and exercise well, I won't be able to continue its upkeep…
The more often you code websites, the more often it's likely you'll need to solve the same problem again and again. Let's consider a typical example. Horizontal navigation bars are often made up of a number of equally spaced <li>
links. Suppose we need margin to the left and right side of each list item, except for the first and last list item. Historically, we have been able to solve this problem by adding a semantically superfluous classname to the first and last <li>
elements in the list, as shown in the highlighted lines in the following code snippet:
<ul> <li class="first"><a href="#">Why?</a></li> <li><a href="#">Synopsis</a></li> <li><a href="#">Stills/Photos</a></li> <li><a href="#">Videos/clips</a></li> <li><a href="#">Quotes</a></li> <li class="last"><a href="#">Quiz</a></li> </ul>
And then by adding a couple of rules in the CSS, we can amend the margin for those two list items:
li { margin-left: 5%; margin-right: 5%; } .first { margin-left: 0px; } .last { margin-right: 0px; }
This works but isn't flexible. For example, when building a website built on a CMS system, list items for linking new content might be added automatically, so it might not
be a simple task to add or remove the last
or first
class to the correct list item in the markup.
CSS2.1 already had a selector applicable for the first item in a list:
li:first-child
However, CSS3 adds a selector that can also match the last:
li:last-child
Using these selectors together, we don't need any additional classes in our markup.
We'll fix up our And the winner isn't... site navigation using this and a combination of the display: table
property. The following screenshot shows how things look currently:
Now, let's take a look at the graphic mockup:
The navigation bar links span the full width of the design, which we need to replicate. Our markup for the navigation looks like this:
<nav role="navigation"> <ul> <li><a href="#">Why?</a></li> <li><a href="#">Synopsis</a></li> <li><a href="#">Stills/Photos</a></li> <li><a href="#">Videos/clips</a></li> <li><a href="#">Quotes</a></li> <li><a href="#">Quiz</a></li> </ul> </nav>
First, we'll set the nav
element to be a table
:
nav { display: table; /* more code... */ }
Then the <ul>
to be displayed as a table-row
:
nav ul { display: table-row; /* more code... */ }
And finally the list-items to display as table-cells
:
nav ul li { display: table-cell; /* more code... */ }
This means that if extra list items are added, they will automatically space themselves accordingly. Finally, we'll use our CSS selectors to align the text to the right and left of the first and last list items:
nav ul li:last-child { text-align: right; } nav ul li:first-child { text-align: left; }
Then in the browser, our navigation is approaching our original composite:
Don't worry; these tables are only for display!
You may be wondering what on earth I'm thinking of, to suggest that we use a table for the navigational layout. However, don't forget, these tables are only presentational. That means they exist only in the CSS and are nothing to do with the markup. We are merely telling the browser we want those elements to appear and behave as if they were a table, not actually be a table. Displaying the markup in this manner also doesn't preclude us from using a different layout type for a different viewport, for example, display: inline-block
for viewports below 768 px.
But what about those alternate colors shown in the navigation bar links of the original composite? Again, CSS3 has a selector that can solve this problem for us without the need for additional markup:
:nth-child(even)
Let's use this selector to fix the problem and then we can look at some of the many ways that nth-child can solve problems that previously required extra markup. I'll add alternate red links in the navigation bar by adding the following style rule:
nav ul li:nth-child(even) a { color: #fe0208; }
And now we have alternate colors in the navigation links:
How about that? Not a line of jQuery in site and no extra markup! What did I tell you? CSS3 selectors are great!
Amongst frontend web developers and designers, nothing makes mathematics weaklings tremble quite like the nth-based rules (well, you know, except maybe someone asking you to code a little PHP or give them a hand with some REGEX expressions). Let's see if we can make sense of the beast and gain a little respect from those backend wizards.
When it comes to selecting elements in the tree structure of the DOM (Document Object Model or more simplistically, the elements in a page's markup) CSS3 gives us incredible flexibility with a few nth-based rules—:nth-child(n)
, :nth-last-child(n)
, :nth-of-type(n)
, and :nth-last-of-type(n)
. We've seen that we can use (odd)
or (even)
values (as we have to fix our navigation above) but the (n)
parameter can be used in another couple of ways:
:nth-child(2)
—would select the second item:nth-child(3n+1)
—would start at 1
and then select every third elementThe integer based property is easy enough to understand, just enter the element number you want to select. The numeric expression version of the selector is the part that can be a little baffling for mere mortals. Let's break it down. For practicality, within the brackets, I start from the right. So, for example, if I want to figure out what (2n+3)
will select, I start at the right (from the third item) and know it will select every second element from that point on. I've amended our navigation rule to illustrate this:
nav ul li:nth-child(2n+3) a { color: #fe0208; }
As you can see, the third list item is colored and then every subsequent second one after that (if there were 100 list items, it would continue selecting every second list item):
How about selecting everything from the second item onwards? Well, although you could write :nth-child(1n+2)
, you don't actually need the first number 1
as unless otherwise stated, n
is equal to 1
. We can therefore just write :nth-child(n+2)
. Likewise, if we wanted to select every third element, rather than write :nth-child(3n+3)
, we can just write :nth-child(3n)
as every third item would begin at the third item anyway, without needing to explicitly state it.
The expression can also use negative numbers for example, :nth-child(3n-2)
starts at minus 2 and then selects every third item. Here's our navigation amended with the following rule:
nav ul li:nth-child(3n-2) a { color: #fe0208; }
And here's what it gives us in the browser:
Hopefully, that's making perfect sense now?
The child
and last-child
differ in that the last-child
variant works from the opposite end of the document tree. For example, :nth-last-child(-n+3)
starts at 3 from the end and then selects all the items after it. Here's what that rule gives us in the browser:
Finally, let's consider :nth-last-of-type
. Whilst the previous examples count any children regardless of type, :nth-last-of-type
let's you be specific about the type of item you want to select. Consider the following markup:
<ul> <li class="internal"><a href="#">Why?</a></li> <li><a href="#">Synopsis</a></li> <li class="internal"><a href="#">Stills/Photos</a></li> <li class="internal"><a href="#">Videos/clips</a></li> <li class="internal"><a href="#">Quotes</a></li> <li class="internal"><a href="#">Quiz</a></li> </ul>
Note that the second list item doesn't have the internal
class added to it.
Consider the following rule:
nav ul li.internal:nth-of-type(n+2) a { color: #fe0208; }
You can see that we are telling the CSS, "From the second matching item, target every <li>
item with a class called internal
. And here's what we see in the browser:
Another handy selector is the negation pseudo-class selector. This is used to select everything that isn't something else. For example, keeping the same markup as the previous example, if we change our rule as follows:
nav ul li:not(.internal) a { color: #fe0208; }
You can see that we are opting to select every list item that doesn't have the internal
class . So in the browser, we see this:
So far we have looked primarily at what's known as structural pseudo-classes (full information on this is available at http://www.w3.org/TR/selectors/#structural-pseudos). However, CSS3 has many more selectors. If you're working on a web application, it's worth looking at the full list of UI element states pseudo-classes (http://www.w3.org/TR/selectors/#UIstates), as they can; for example, help you target rules based on whether something is selected or not.
Pseudo-elements have been around since CSS2 but the CSS3 specification revises the syntax of their use very slightly. To refresh your memory, until now, p:first-line
would target the first line in a <p>
tag. Or p:first-letter
would target the first letter. Well, CSS3 asks us to separate these pseudo-elements with a double colon to differentiate them from pseudo-classes. Therefore, we should write p::first-letter
instead. Note that however Internet Explorer 8 and lower versions don't understand the double colon syntax; they understand only the single colon syntax.
One thing that you may find particularly handy about the :first-line
pseudo-element
is that it is specific to the viewport. For example, if we write the following rule:
p::first-line { color: #ff0cff; }
As you might expect, the first line is rendered in an awful shade of pink (I was thinking of Moulin Rouge at the time):
However, on a different viewport, it renders a different selection of text:
So, without needing to alter the markup, with a responsive design, there's a handy way of having the first visual (as the browser renders it, not as it appears in the markup) line of text appear differently than the others.
Hopefully this brief foray into CSS3 selectors illustrates how they help keep a responsive design and code base free of additional markup. It the past, I've needed to use a JavaScript library such as jQuery to make complicated selections but CSS3 often negates that need. It's also comforting to know that the CSS3 selectors module is already at the W3C Recommendation status; so it's a very mature module that's unlikely to change much from here on.
18.224.59.145