A friendly web development tutorial for plucking out HTML elements
Way back in the Links and
Images chapter, we learned how to connect an HTML document to other files
in our project. “CSS selectors” are similar, except instead of
navigating between whole files, they let us map a single CSS rule to a specific
HTML element. This makes it possible to selectively style individual
elements while ignoring others.
Unless you want every section of your website to look exactly the same, this
is a crucial bit of functionality for us. It’s how we say things like
“I want this paragraph to be blue and that other paragraph to be
yellow.” Until now, we’ve only been able to turn all our
paragraphs blue (or yellow).
The only CSS selector we’ve seen so far is called the “type
selector”, which targets all the matching elements on a page. In this
chapter, we’ll explore more granular ways to style a web page with class
selectors, descendant selectors, pseudo-classes, and ID selectors.
Setup
We’ll only need one HTML file and a CSS stylesheet for our example
this chapter. Create a new folder called css-selectors and new web
page called selectors.html with the following markup:
<!DOCTYPE html><htmllang='en'><head><metacharset='UTF-8'/><title>CSS Selectors</title><linkrel='stylesheet'href='styles.css'/></head><body><h1>CSS Selectors</h1><p>CSS selectors let you <em>select</em> individual HTML elements in an HTML
document. This is <strong>super</strong> useful.</p><p>Classes are ridiculously important, since they allow you to select
arbitrary boxes in your web pages.</p><p>We’ll also be talking about links in this example, so here’s
<ahref='https://internetingishard.com'>Interneting Is Hard</a> for us to
style.</p><div>Button One</div></body></html>
Go ahead and create that styles.css stylesheet in the same
folder, too. This gives us everything we need to explore CSS selectors.
If you’re just diving into this tutorial series, be sure to have a
quick read through the Introduction to get set
up with the Atom text editor.
Class Selectors
“Class selectors” let you apply CSS styles to a specific HTML
element. They let you differentiate between HTML elements of the same type,
like when we had two <div> elements in the previous chapter, but
only wanted to style one of them. Class selectors require two things:
A class attribute on the HTML element in question.
A matching CSS class selector in your stylesheet.
We can use a class selector to style the first paragraph of our example page
differently than the rest of them. This could be, for instance, the synopsis of
a newspaper article. First, let’s add a class attribute to
the desired paragraph:
<pclass='synopsis'>CSS selectors let you <em>select</em> individual HTML
elements in an HTML document. This is <strong>super</strong> useful.</p>
Now, we can pluck out that <p class='synopsis'> element
in our CSS with the following (add this to styles.css):
This rule is only applied to elements with the corresponding
class attribute. Notice the dot (.) prefixing the
class name. This distinguishes class selectors from the type selectors that
we’ve been working with before this chapter.
Class Naming Conventions
The value of the HTML class attribute can be (almost) anything
you want as long as it matches the selector in your CSS. The standard naming
convention for classes is to use all lowercase and hyphens for spaces, just
like file and folder names.
Adding a class attribute doesn’t alter the semantic
meaning of your HTML document at all—it’s purely for hooking into
your CSS stylesheet. However, it’s still usually a good idea to avoid
naming classes based on their appearance. If we chose to name our class
.italic, we wouldn’t be able to do much besides make it
italic in our CSS without leading to a confusing situation. Using something
semantic like .synopsis gives us more freedom for our CSS to
customize how that synopsis is displayed.
More Useful Divs
The class attribute isn’t limited to
<p> elements—it can be defined on any HTML
element. So, armed with CSS class selectors, our generic
<div> and <span> boxes from the previous
chapter become much, much more useful. We can use them to style both individual
elements as well as arbitrary sections of our web page.
Let’s start with individual elements by recreating our button from the
previous chapter. This time, we’ll use a class instead of a
div selector. Add the following to styles.css:
.button {
color: #FFF;
background-color: #5995DA; /* Blue */font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063; /* Dark gray */border-radius: 5px;
width: 200px;
margin: 20px auto;
}
Of course, we need a corresponding class attribute for this to
work. Change the <div> in selectors.html to
match the following:
<divclass='button'>Button One</div>
Unlike the previous chapter that styled all the
<div> elements, this lets us use it for other things besides
buttons.
Container Divs
Remember that <div> doesn’t alter the semantic
structure of a page. This makes it a great tool for defining the
presentational structure of a web page. By wrapping other HTML
elements in <div> tags, we can organize our site into larger
layout-oriented chunks without messing up how search engines view our content.
For example, let’s try to create a fixed-width layout using the auto-margin
technique that we learned in the previous chapter. First, wrap our entire
document in a generic <div> and give it a unique class:
<body><divclass='page'><!-- Add this --><h1>CSS Selectors</h1><pclass='synopsis'>CSS selectors let you <em>select</em> individual HTML
elements in an HTML document. This is <strong>super</strong> useful.</p><p>Classes are ridiculously important, since they allow you to select
arbitrary boxes in your web pages.</p><p>We’ll also be talking about links in this example, so here’s
<ahref='https://internetingishard.com'>Interneting Is Hard</a> for us to
style.</p><divclass='button'>Button One</div></div><!-- And this --></body>
Then, add the following to styles.css:
.page {
width: 600px;
margin: 0 auto;
}
No matter how you resize the browser window, our web page will always be 600
pixels wide and centered in the available space. Note that this was the exact
same way we centered our button, but now we’re doing it to multiple
elements at the same time by nesting them in a generic container.
This is how layouts are defined in more complex web pages. For instance, if
our page had a sidebar, we would nest all the sidebar elements in
another<div> with a .sidebar class.
We’ll see this in action in the next
chapter. For now, the key takeaway is that without class selectors to
differentiate our <div> elements, none of this would be
possible.
Reusing Class Styles
The same class can be applied to multiple elements in a single HTML
document. This means that we can now reuse arbitrary CSS declarations wherever
we want. To create another button, all we have to do is add another HTML
element with the same class:
This gives us a second button that looks just like the first
one—without writing a single line of CSS! Organizing similar graphical
elements into reusable CSS rules like this makes life much easier as a web
developer. If we ever wanted to, say, change the button color, we would only
have to do it in one place and all our buttons would automatically update.
Modifying Class Styles
What if we want to alter our second button a little bit? Fortunately, we can
apply multiple classes to the same HTML element, too. The styles from
each class will be applied to the element, giving us the opportunity to both
reuse styles from .button and override some of them with a new
class.
Go ahead and add another class to our second button with the following
markup. Notice how multiple classes live in the same class
attribute, separated by spaces:
This element now has two separate classes, and we can use either of them to
style it. This opens up some options. Styles shared by both buttons can live in
the .button class (as they already do), and styles specific to the
second button reside in the .call-to-action class (be sure to add
this after the .button rule):
There’s a couple of important things going on with our second button
now:
It’s adding a newfont-style declaration to
the original .button rule.
It’s overriding an existing background-color
style from .button.
Overriding occurs because of the order of .call-to-action and
.button in our stylesheet. When there’s two conflicting
properties in a CSS file, the last one is always the one that gets applied. So,
if you moved .call-to-action to the top of
styles.css, .button would have the final word on the
value of background-color, and it would remain blue.
This means that the order of the class attribute in our HTML
element has no effect on override behavior. Multiple classes on a single
element are applied “equally” (for lack of a better term), so the
precedence is determined solely by the order of the rules in
styles.css. In other words, the following elements are effectively
equivalent:
<!-- These result in the same rendered page --><divclass='button call-to-action'>Button Two</div><divclass='call-to-action button'>Button Two</div>
This does, however, get more complicated when CSS specificity is involved,
which we’ll discuss at the end of this chapter.
Descendant Selectors
You may have noticed that the <em> in our first paragraph
is no longer distinguishable from its surround text, since our
.synopsis rule made everything italic.
To alter that <em> element, we could add another class
directly to it, but that won’t result in very maintainable code. We want
to treat .synopsis as its own independent component that we can
style entirely from CSS (i.e., without requiring alterations to our HTML just
for the sake of styling something.)
This is what “descendant selectors” are for. They let you target
only those elements that are inside of another element. For example,
we can pull out that <em> in the .synopsis
paragraph with the following:
.synopsisem {
font-style: normal;
}
Adding this rule to styles.css will make the
<em> display as upright (roman) characters, thus
differentiating it from the italics we put on the entire <p>
text. The rest of the <em> elements on the page will be
unaffected.
Descendant selectors aren’t limited to class selectors—you can
combine any other group of selectors this way. For instance, if we wanted to
select only <em> elements inside of headings, we might use
something like this:
h1em {
/* Some other styles */
}
Again, the goal of this chapter is to let you apply styles to exactly the
element you want. Descendant selectors are a great tool towards this end. You
may also want to check out the related “child selector” over
at MDN if you’ve still got room in your toolbox.
Don’t Overdo It
You can nest descendant selectors as deep as you want, but don’t get
carried away. Life gets confusing and terrible when you start writing rules
that look like this:
/* Try to avoid this */.articleh2.subheadingem {
/* Special styles */
}
This isn’t the least bit reusable because it matches only the
following HTML structure:
<divclass='article'><h2><spanclass='.subheading'>This is <em>really</em> special text</span></h2></div>
If you ever wanted to apply these styles to an <h2>
heading that’s not wrapped in <div class='article'>
tags, you’re kind of screwed. Same deal if you want to apply them to an
<h3> heading anywhere on the page. This kind of CSS also
leads to a specificity nightmare.
Pseudo-Classes for Links
So far, all the CSS selectors we’ve seen map directly to a piece of
HTML markup that we wrote. However, there’s more going on in a rendered
web page than just our HTML content. There’s “stateful”
information about what the user is doing (opposed to the content we’ve
authored).
The classic example is a link. As a web developer, you create an <a
href> element. After the browser renders it, the user can interact
with that link. They can hover over it, click it, and visit the URL.
CSS “pseudo-classes” provide a mechanism for hooking into this
kind of temporary user information. At any given time, an <a
href> element can be in a number of different states, and you can use
pseudo-classes to style each one of them individually. Think of them as class
selectors that you don’t have to write on your own because they’re
built into the browser.
Basic Link Styles
Pseudo-classes begin with a colon followed by the name of the desired
class. The most common link pseudo-classes are as follows:
:link – A link the user has never visited.
:visited – A link the user has visited before.
:hover – A link with the user’s mouse over it.
:active – A link that’s being pressed down by a
mouse (or finger).
Let’s take a look at all of these by adding the following rules to our
CSS stylesheet (also note our use of keyword
colors instead of our usual hex codes):
If you’ve never been to the InternetingIsHard.com home
page, you should see a blue link. Otherwise, you’ll see a purple link.
When you hover over the link, it will turn aqua, and when you push down on it,
it’ll turn red.
Visited Hover State
The above snippet is just fine for most websites, but take a closer look at
the a:visited behavior by changing the href attribute
to a URL that you’ve been to before. Our a:hover style is
applied to both visited and unvisited links. We can refine our links even more
by stringing pseudo-classes together. Add this below the previous snippet:
a:visited:hover {
color: orange;
}
This creates a dedicated hover style for visited links. Hovering over an
unvisited link changes it to aqua, while hovering over a visited link will turn
it orange. Fantastic! Except for the fact that this breaks our
a:active style due to some complicated CSS internals that
you’ll never want to read about. When you click down, our link
won’t turn red anymore.
Visited Active State
We can fix that with a:visited:active. Add the following to the
end of our stylesheet. Note that, as with our .call-to-action
class, the order in which these are defined in styles.css
matters:
a:visited:active {
color: red;
}
These last two sections let you style visited links entirely separately from
unvisited ones. It’s a nice option to have, but again, you’re
welcome to stop at basic link styles if that’s all you need.
Pseudo-Classes for Buttons
Pseudo-classes aren’t just for styling text links—they can be
applied to any kind of selector (not just type selectors). Instead of styling
a:link and friends, we’ll be modifying our
.button class with pseudo-classes in this section. This will let
us create buttons that actually go somewhere.
Link Elements, Not Div Elements
First, we need change our buttons to be <a href> elements
instead of generic <div> elements, as shown below:
If you reload this in your browser, you’ll see that we lost some of
our styles even though we’re using the same classes. This is because
<a> is an inline
element by default and also has a default color value.
We need to change it to a block element and remove some of the default link
styling.
Button Styles
Let’s start with :link and :visited
variants. We’re using a similar pattern as in the previous section, but
since these are buttons, we want to keep both the unvisited and visited color
the same. Change the existing .button rules to match the
following:
.button:link, /* Change this */.button:visited { /* Change this */display: block; /* Add this */text-decoration: none; /* Add this */color: #FFF; /* The rest is the same */background-color: #5995DA;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063;
border-radius: 5px;
width: 200px;
margin: 20px auto;
}
Notice the new :link and :visited pseudo-classes
in the selector. Without it, our color would not override the
browser’s default a:link style. CSS specificity explains why this is the case in
greater detail. Next, let’s do the hover states:
Both our buttons will be a lighter blue on hover. Finally, let’s make
it a little darker when the user presses the mouse down with the
:active pseudo-class:
The great part about this is that all the styles we just defined are
entirely reusable. Stick a .button class on any HTML
element, and you’ll turn it into an interactive button.
The Other Button
Now, what about that second button? It’s supposed to have a yellow
background, but we broke it with the code from the previous section. Our
.button:link selector was more “specific” than our
current .call-to-action rule, so it took precedence. Again,
we’ll explore this further at the end of the chapter.
For now, let’s fix it by applying some pseudo-classes to our
.call-to-action rule. Replace the existing rule with the following
(make sure this appears after the new .button styles from
the previous section):
Since we only added the .call-to-action class to our second
button, that’s the only one that’ll turn yellow. Of course, we still need the
.button class on both <a> elements because it
defines shared styles like the padding, border radius, and font weight.
Pseudo-Classes For Structure
Link states are just one aspect of pseudo-classes. There’s also a bunch
of other pseudo-classes that provide extra information about an
element’s surroundings. For example, the :last-of-type
pseudo-class selects the final element of a particular type in its parent
element. This gives us an alternative to class selectors for selecting specific
elements.
For instance, we could use :last-of-type to add some space
after the last paragraph of our example page:
p:last-of-type {
margin-bottom: 50px;
}
This avoids selecting the first two <p> elements
without requiring a new class attribute on the last
paragraph:
We could even use a :first-of-type pseudo-class in place of our
.synopsis class. Replacing the existing .synopsis
rule with the following snippet should result in the exact same page.
There’s pros and cons to using this method over plain old classes. For
instance, this only works if our synopsis is a <p>
element. If we ever wanted to create a multi-paragraph synopsis by wrapping a
bunch of <p> elements in a <div
class='synopsis'>, we’d have to rewrite our CSS
accordingly. On the other hand, the pseudo-class method lets us style
specific elements without having to alter the HTML at all. This gives us a
very clean separation of content from presentation.
Caveats
Ok, so actually the pseudo-class method is a little more complicated.
They’re still a useful tool—as long as you know their ins-and-outs.
The :first-of-type and :last-of-type selectors only
operate inside their parent element. That is to say,
p:first-of-type selects the first <p> in
every container element.
We have a single generic <div> wrapping our content
(.page), so this isn’t a problem for us. However, consider what
happens when we add this to the bottom of our .page element:
<divclass='sidebar'><p>If this page had a sidebar...</p><p>We’d have some problems with pseudo-classes.</p></div>
We won’t be able to make a real sidebar until the next chapter, but this does highlight the
complications of pseudo-classes for structure. The first <p>
element here will also match p:first-of-type because the
pseudo-class’s scope is limited to the parent element.
If you wanted to avoid the sidebar paragraphs and select only the first
<p> in our <div class='page'>, you would
need to limit its scope using a child
selector, like so:
All of this is yet another example of how there are many ways to do the same
thing in the wonderful world of HTML and CSS. Different developers adhere to
different schools of thought. Some like the semantic nature of pseudo-classes,
while others go to the far extreme with explicit class attributes
on every HTML element.
ID Selectors
“ID selectors” are a more stringent alternative to class
selectors. They work pretty much the same way, except you can only have
one element with the same ID per page, which means you can’t
reuse styles at all. Instead of a class attribute, they require an
id attribute on whatever HTML element you’re trying to
select. Try adding one to our second button:
The corresponding CSS selector must begin with a hash sign (#)
opposed to a dot. Adding the following to styles.css will change
the text color of our yellow button:
#button-2 {
color: #5D6063; /* Dark gray */
}
The problem is, if we wanted to share this style with another button,
we’d have to give it another unique id attribute. Pretty
soon, our CSS would start to look pretty gnarly:
/* (This is painful to maintain) */#button-2,
#button-3,
#checkout-button,
#menu-bar-call-to-action {
color: #5D6063;
}
For this reason, ID selectors are generally frowned upon. Use class
selectors instead.
URL Fragments
id attributes need to be unique because they serve as the
target for “URL fragments”, which we sort of glossed over in our discussion
of URLs. Fragments are how you point the user to a specific part of a web
page. They look like an ID selector stuck on the end of a URL.
For example, if we wanted to point the user to our second button, we could
use the following. Note that we can omit the URL entirely if we’re
linking to a different section on the same page:
<!-- From the same page --><ahref='#button-2'>Go to Button Two</a><!-- From a different page --><ahref='selectors.html#button-2'>Go to Button Two</a>
If you add the first option to our selectors.html page and
click it, you’ll see the URL in the browser change. To actually see it
jump down to the second button, you’ll need to add some more dummy
content to the page or make the window height very short, as the browser will
limit scrolling to the visible page.
This overlapping functionality is more reason to avoid ID selectors. They
create a dependency between your website’s URLs and your CSS styles. Imagine
using a bunch of id attributes on your headings as both URL
fragments and ID selectors. If you forgot to update your stylesheet every time
you edited the URL of a section, you would actually break your website.
CSS Specificity
Earlier in this chapter, we talked about how order matters when it comes to CSS rules
in an external stylesheet. All else being equal, rules are applied from
top-to-bottom. This allowed us to override rules in a predictable manner.
Unfortunately, not all CSS selectors are created equal. “CSS
specificity” is the weight given to different categories of selectors.
This means that certain selectors will always override other ones,
regardless of where they appear in the stylesheet.
Let’s start by seeing where this doesn’t break. If you
add the following after our existing .call-to-action rules, it
will override the previous background-color. If you stick it at
the top of the file, it’ll get overridden later on, so our button
won’t turn red. This is expected behavior.
.call-to-action:link,
.call-to-action:visited {
background-color: #D55C5F; /* Red */
}
Now, watch what happens when we try to do the same thing with an ID
selector. First, be sure to delete the previous snippet, then try adding this
before our existing .call-to-action rules:
#button-2 {
background-color: #D55C5F; /* Red */
}
ID selectors have higher specificity than class selectors, so this
will turn our second button red even though we try to set the
background-color with .call-to-action:link later in
our stylesheet. The whole “order matters” concept only works when
all your rules have the same specificity.
The specificity of selectors we’ve seen in this chapter are show
below, from greatest to least:
#button-2
.button:link and .synopsis
em (they’re equal)
.button
a:link
a
This can get very confusing. It’s such a big problem that an entire
methodology called “BEM” has evolved. BEM attempts
to make CSS rules more reusable by making everything a class selector.
This completely eliminates the potential for specificity issues.
BEM is outside the scope of this tutorial. The takeaway here is that CSS
rules are not necessarily applied in sequential order, but you should try to
make the browser do so by writing CSS that uses the same specificity.
Summary
In this chapter, we got some hands-on experience with class selectors,
descendant selectors, pseudo-classes, link styling, and ID selectors. The goal
of all this was to be able to target a specific HTML element from your CSS.
Class selectors are by far the most versatile and come with the least amount of
drawbacks. As a result, they’ll become part of your daily life as a web
developer.
Like it or not, things got a lot more complicated this chapter. We’re
now able to make our CSS interact with an HTML document in half a dozen
different ways. Furthermore, over the next few chapters, we’ll begin to
see a dependency between our HTML’s structure and the layout of a web
page. With all this interplay between CSS and HTML, it can be hard to know
where to start building a new web page.
The separation
of content from presentation helps guide this process. You need content
before you can present it, so your first step is usually to mark up your raw
content with HTML tags. Once that’s prepared, you’re ready to add
class attributes to your elements and style them one-by-one. When
you discover a need for some extra structure to create a desired layout (e.g.,
turn a group of elements into a sidebar), that’s when you start wrapping
your content in container <div>’s.
This chapter covered almost all the CSS selectors that power real websites.
We’ve got the tools we need to dive much deeper into complex CSS layouts. In
the next installment of HTML and CSS Is
Hard, we’ll learn how to create columns and sidebars using CSS
floats.