Selectors & Rulesets
In CSS, styles are applied to elements through rules. In the previous code
showing embedded styles, you saw an example of a ruleset which contains
multiple rules grouped in a block enclosed by curly braces ({}
). The code
listing below shows various parts that make up a ruleset.
A ruleset starts with a selector, the mechanism by which certain elements are targeted for application of the given rules.
selector { /* <-- ruleset, enclosed in curly braces */
property-1: value1; /* <-- single rule definition */
property-2: value2;
/* ... */
property-n: valuen;
}
In the below example, we’re telling the browser to select all p
elements and
give them a font size of twice the document’s root font size, in red Arial font,
with a 2px wide solid blue border around each.
p {
font-size: 2rem;
color: red;
font-family: 'Arial';
border: 2px solid blue;
}
Simple Selectors
In this section we will will go over some of the more commonly used selectors. Read through the below descriptions of selectors in example code snippets. Selector descriptions are presented as code comments inside of each selector’s respective block.
Universal
The universal selector, a.k.a. wildcard selector, is simply an asterisk
(*
). It selects all normal elements.
* {
/* matches all elements */
}
Type (Element)
The type selector, also commonly referred to as an element selector, simply uses the tag name of an HTML element to target all elements of that type. For example,
p {
/* matches all <p> (paragraph) elements */
}
table {
/* matches all <table> elements */
}
Class
A class selector starts with a dot (.
) immediately followed by a class name
to target. Used alone, a class selector will target all elements which have that
class, regardless of their element type.
.foo {
/* matches all elements with class "foo" */
}
/* equivalent to */
*.foo {
/* ... */
}
ID
An id selector starts with a hash mark (#
) immediately followed by the id
of the element to target.
#bar {
/* matches the element with id="bar" */
}
Attribute
As discussed before, HTML elements can optionally have attributes, such as the
href
attribute on links:
<p>Here is a <a href="http://wikipedia.org">link to Wikipedia</a></p>
The attribute selector lets us target CSS rulesets to elements based on the
existence or value of attributes on elements, optionally using some rudimentary
substring matching. The attribute selector is contained within square brackets
([]
), consisting of the name of the attribute optionally followed by a
matching operator and value.
Take a look at the following attribute selectors and their descriptions, and try to figure out which ones would apply to the Wikipedia link in the example above.
a[href] { /* <a> elements with an href attribute, whatever its value */ }
a[href="wiki"] { /* ... href attribute exactly equal to "wiki" */ }
a[href^="wiki"] { /* ... starts with (or equal to) "wiki", e.g. "wiki123" */ }
a[href$="wiki"] { /* ... ends with (or equal to) "wiki", e.g. "123wiki" */ }
a[href*="wiki"] { /* ... contains substring "wiki", e.g. "12wiki34" */ }
a[href~="wiki"] { /* ... space-split attribute includes "wiki" exactly,
e.g. "some wiki string" */ }
/* add an `i` flag on the end of any of these
to make the search case-insensitive */
a[href="wiki" i] { /* ... case-insensitive exact match, e.g. "WIki" */ }
show answer
Answer: Only the selectors on lines 1 and 9 apply.
How might this change if instead href="wikipedia.org"
?
Be mindful, though, if working with boolean attributes:
<div someAttr="false"></div>
On this div
element, the (fictional) attribute someAttr
is not set to the
boolean false
, but to the string "false"
. As such, the following selector
will match:
div[someAttr] {
/* matches <div someAttr="false"> */
}
Combinators
Simple selector combinators offer a mechanism to combine multiple simple selectors into one more specific selector that can be used on a ruleset when we want to target elements more exactly than single selectors alone would allow. The type of combinator used defines the relationship between the simple selectors that it combines.
In all cases, though intermediate elements affect whether the entire selector applies, the rules are applied only on the elements ultimately matching the rightmost simple selector.
General Descendant
Combine selectors with whitespace in order to target general descendants – children, grandchildren, etc.
p a {
/* all <a> elements inside of <p> elements */
}
<p>
Text. <a>Targeted.</a> <span><a>Also targeted.</a></span>
</p>
Direct Child
Combine selectors with a single right angle bracket (>
) to target only direct
children (not including grandchildren, etc.).
p > a {
/* all <a> which are children of <p> elements */
}
p>a {
/* whitespace around the > sign is not significant, fyi */
}
<p>
Text. <a>Targeted.</a> <span><a>Not targeted.</a></span>
</p>
General Sibling
Select an element if it follows a sibling that matches a given selector. (Two elements are siblings if they share the same parent element).
img ~ p {
/* all <p> elements that occur after an <img> sibling */
}
<div>
<p>Not targeted</p>
<img />
<div></div>
<p>Targeted</p>
<p>Targeted</p>
</div>
Adjacent Sibling
Select an element if it immediately follows a sibling matching a given selector.
img + p {
/* all <p> elements that immediately follow an <img> element */
}
<div>
<p>Not targeted</p>
<div></div>
<img />
<p>Targeted</p>
<p>Not targeted</p>
</div>
Pseudo-classes
A pseudo-class is an addendum that can be attached to a simple selector using
a single colon followed by the name of the pseudo-class. In the examples below,
we will use a substitute simple selector E
to demonstrate the pseudo-class
selectors. Note that there is not any whitespace between a simple selector and
its pseudo-class(es).
Structural pseudo-classes
Structural pseudo-classes allow for matching elements based on their position in the document in ways that cannot otherwise be targeted using simple selectors and combinators alone.
:*-child
The :*-child
pseudo-classes match elements based on their position and
relationship to their sibling elements.
E:first-child {
/* matches an element which is the first child of its parent */
}
E:last-child {
/* ... the last child of its parent */
}
E:only-child {
/* ... the only child of its parent */
}
There is a special :nth-child(An+B)
functional-notation selector which takes a
formula as a single argument. It will match elements based on their order in the
document following a pattern established by this formula. It is best explained
by the official spec:2
This syntax is referred to as the
An+B
notation, and represents an integer step (A
) and offset (B
). It indicates theAn+B
th elements in a list, for every positive integer or zero value ofn
, with the first element in the list having index 1 (not 0).For values of
A
andB
greater than 0, this effectively divides the list into groups ofA
elements (the last group taking the remainder), and selecting theB
th element of each group.The
An+B
notation also accepts theeven
andodd
keywords, which have the same meaning as2n
and2n+1
, respectively.
Distinguishing between even and odd element positions is a common use case for this mechanism. A typical scenario where this is used is for making striped table rows – that is, for a given table, we want the background color of each row to alternate between light and dark, making it easier to read values aligned across many wide rows.
tr:nth-child(2n) {
background-color: white;
}
tr:nth-child(2n + 1) {
background-color: lightgrey;
}
There is also an :nth-last-child(An+B)
pseudo-class which works similarly to
the :nth-child()
selector, except it counts elements in reverse: the last
element is at index 1, the next-to-last element is at index 2, and so on.
:*-of-type
The :*-of-type
pseudo-classes act much like the :*-child
pseudo-classes,
except instead of counting all siblings, it only considers siblings of the same
element type (element tagname).
E:first-of-type {
/* E elements which are the first of their type amongst their siblings */
}
E:last-of-type {
/* ... last of their type amongst their siblings */
}
E:only-of-type {
/* E elements that have no siblings of their same type */
}
E:nth-of-type(An + b) {
/* see `An + B` notation above */
}
E:nth-last-of-type(An + b) {
/* see `An + B` notation above */
}
:empty
The pseudo-class :empty
matches an element that has no children and no
content.
Dynamic pseudo-classes
Dynamic pseudo-classes are concerned with element states that might not
necessarily be derivable from the document tree structure itself. These are
relatively transient as they can change with user interaction, and may be unique
to the type of element on which they apply. For example, <input>
elements have
an available pseudo-class of :disabled
which will match when the input is
disabled, and <a>
elements have an available pseudo-class of :visited
which
will match when the user has already visited the linked resource.
a:visited {
/* links that have been visited */
}
div:hover {
/* <div> element when the mouse is over it */
}
input[type='checkbox']:checked {
/* checked checkboxes */
}
#myButton:enabled:disabled {
/* valid selector, but will never match! ...
... because :enabled and :disabled are mutually exclusive */
}
Negation pseudo-class
The negation pseudo-class :not()
uses functional notation to accept a simple
selector whose selection result will be negated.
a:not([href^="https://"]) {
/* matches all links that do NOT explicitly use https */
}
div > *:not(p):not(ol) {
/* matches all children of a <div> EXCEPT <p> and <ol> elements */
}
Pseudo-elements
Selector groups
Multiple selectors can be grouped onto one ruleset by separating them with
commas (,
) which effectively function as logical
p.highlight,
div > .highlight {
/* rules */
}
may be read as, “target all elements matching (p
element with class
highlight
) highlight
that is a direct
child of a div
element)“.
Another way of thinking about selector groups (and the way they are explained by the spec) is that “a comma-separated list of selectors represents the union of all elements selected by each of the individual selectors in the list.”3
Application of style rules
Style rules are applied to their targeted elements cumulatively – that is, a single element that matches the selectors to multiple rulesets will have the style rules from all of the matching rulesets applied to it. For example, with the following CSS,
div {
color: green;
}
*.smallText {
font-size: 10px;
}
and the following element in the document,
<div class="smallText">Lorem ipsum dolor sit amet…</div>
the element will have text that is both green and 10 px in size. Because it matches both selectors, the rules from both rulesets are applied.
In the next chapter, we will see how the browser handles conflicting rules that prescribe different values for the same style property.