loading...

CSS selectors reviewed with examples

willamesoares profile image Will Soares ・8 min read

header-image

Have you ever find yourself struggling to use CSS selectors to style an element that is the first sibling of the nth child of an element that contains an attribute with a specific value, or something like that? Well, if you have worked with dynamic layouts you have probably been through that.

This is intended to be a quick reference for CSS selectors, attributes, and pseudo-classes, presented with examples. I wanted to do this in a private document for personal use since there are tons of CSS cheat sheets out there. But, maybe I will get to put it in a different way, which can be more understandable to someone out there. So, why not share it with everyone?

For this post, there are a few basic concepts you must be aware of before continuing, those being selectors (type, attribute, id, class), pseudo-classes, pseudo-elements, and combinators.

Let's get started.

Selectors

Even though CSS tutorials often consider everything we are going to talk about here as CSS selectors, it is good to point that there is a specific term for each type of selectors in a CSS stylesheet.

Type selectors

These are selectors used to match elements in the DOM, such as p, div, span, and even customized tags, like in the example below.

type-selector

custom {
  border: 1px solid black;
}

p {
  color: green;
}
<custom>Custom element</custom>

<p>Paragraph</p>
Class selectors

These are used to select elements in the DOM that have a specific class as an attribute. For this, you have to add a . to the beginning of the name you want to match.

class-selector

.bordered {
  border: 1px solid red;
}
<div class="bordered">
    Bordered element.
</div>
Attribute selectors

These will match elements that have a specific attribute associated with it. This can be used to match both individual attributes and attributes associated with their values. For this type of selector, you must surround the attribute name with square brackets. Check the example below.

attribute-selector

[name] {
  background-color: green;
}

[name="age"] {
  background-color: red;
}
<input type="text" name="name" placeholder="Your name" />
<input type="number" name="age" placeholder="Your age" />

In this example, you can notice that initially, we are creating a CSS rule for all the elements that have an attribute of name. This will give you the two inputs showed in your HTML code since both of them have the attribute. However, after that, we are creating a more specific rule that tells the browser to use a red background for elements that have an attribute of name with a value of age, which will give you the last input.

You can also use different operators to match values, other than using =. Check out them below.

~=

This operator matches a whitespace-separated list of words, in which one of the words is exactly the value passed.

attr-selector-1

[class~="bordered"] {
  border: 3px solid green;
}
<button class="btn bordered">Click Here</button>
|=

This operator matches elements with attribute value being the exact string passed or starting with the string followed by -.

attr-selector-2

[class|="primary"] {
  color: green;
}
<button class="primary-btn">Click Here</button>
<p class="primary">Paragraph</p>
^=

This operator matches elements with attribute value starting with the string passed.

attr-selector-3

[class^="call"] {
  color: green;
}
<button class="caller">Caller</button>
<button class="called">Called</button>
<button class="calling">Calling</button>
$=

This operator matches an element with attribute value ending with the string passed.

attr-selector-4

[class$="ed"] {
  background-color: pink;
}
<div class="required">
    <p>Required</p>
</div>
<div class="validated">
    <p>Validated</p>
</div>
*=

This operator matches an element with attribute value containing the string passed, at least once.

attr-selector-5

[class*="prim"] {
  background-color: pink;
}
<button class="btn-primary">Click here</button>

PS.: in order to perform those matches in a case-insensitive way, you can append an i to the rule, e.g: [attr="value" i].

ID selectors

These will match elements with a specific identifier associated with it. For this type of selector, you have to use a # before the ID you want to match. Check the example below.

id-selector

#d123 {
  font-weight: bold;
}
<div>block without ID</div>
<div id="d123">block with ID of d123</div>
Universal selector

This one is used to match all the elements in the page. It can be used to reset margins and paddings for all elements, for instance. However, it is not indicated since it can overload the rendering process of your page.

universal-selector

* {
  border: 1px solid yellow;
}
<ul>
  <li>
    <a href="#">Item 1</a>
  </li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>
    <span>Item 5</span>
  </li>
</ul>

Pseudo-class

Pseudo-classes are used to match content in a more extensive way since they can refer to elements not directly represented in the DOM, in almost all cases. These type of selectors usually refer to the state of an element. Due to that, they can be dynamically accessed, based on user interaction. Good examples are the :link and :visited pseudo-classes, which are used to style links based on whether they have been visited or not. In order to use a pseudo-class, you have to add a : to the beginning of the class name. As these selectors are hard to represent statically in this post, I think it is better to put them inside a JSFiddle so you can play a little with them if you want. There you can simply uncomment the part of CSS and HTML you want and see the results in the right side tab. Click here to check examples for the selectors listed below (some of them are explained below the table, some are just too intuitive and do not need to be clarified).

  • :focus match elements that are currently focused, meaning when a user clicks to edit an input or textarea element, for instance.

  • :active match the moment when users click an element. The style will be applied for as long as a user holds the click over an element.

  • :hover match elements in which the mouse is currently over.

  • :empty match elements that do not have any child.

  • :valid match inputs that hold legal values according to each input type. One example is when you have an input for email values, so you can apply a style for when a user enters a valid email. Note that an input with no value set is considered being in a valid state.

  • :checked match checkbox inputs that are currently checked.

  • :link match unvisited links.

  • :in-range match input elements that hold values within a specific range.

  • :not(selector) match every element that is not the one passed as a parameter.

  • :first-child match the first child inside of a specific parent. For instance, if you do p:first-child that will match the first paragraph inside of its parent.

  • :first-of-type match the first element of a given type. For instance, if you go simply :first-of-type that will match every element that is the first of its type inside a specific parent.

  • :lang(language) match elements that have an attribute of lang with a specific value.

  • :nth-child(n) match the element that is the nth element inside of a specific parent. For these rules in which you can specify an integer value for the index, you can pass something like 2n to get elements in every two index position, for instance.

  • :nth-last-child(n) same as nth-child but start counting from the end.

  • :nth-of-type(n) match the element that is the nth element of a given type inside a specific parent.

  • :nth-last-of-type(n) same as nth-of-type but start counting from the end.

  • :only-of-type match the element that is the only element of its type inside a specific parent. For instance, if you do p:only-of-type it will match the only existing paragraph, if there are more than one it will not match anything.

  • :only-child match the element that is the only child of a specific parent. If there are more than one children within that parent, nothing will be matched.

  • :optional match input elements with no required attribute.

  • :read-only match elements with a read-only attribute.

  • :read-write match elements with no read-only attribute.

  • :required match elements with a required attribute.

  • :root match the document's root element. This selector is usually used to set global CSS variables.

Pseudo-element

Pseudo-elements can be used to refer to content that does not exist in the source code, i.e, content that is generated afterward. Let's take a look at them below. If you want to check examples for this section, check this fiddle.

  • ::after and ::before used to add and style elements that are respectively the last and first child of the selected element. This is usually used to add tooltips or icons to labels, inputs, etc.

  • ::first-letter select the first letter of a text inside an element.

  • ::first-line select the first line of a text inside an element.

  • ::selection select the current text selected by the user.

Combinators

Combinators are, in my opinion, the most interesting part of CSS selectors. At this point, you will be able to gather everything you learned so far and associate it with combinators to get more complex CSS rules. Let's check the CSS combinators available. Check the examples here.

  • Group of selectors , This can be used when you want to apply the same style to several elements, so instead of creating different blocks for each one you can simply list them using , and write the rules once.
h1, h2, h3, h4, h5, h6 {
  margin: 1rem;
  text-decoration: underline;
}

The rules above apply margin and a text-decoration to all heading tags at once.

  • Descendant selector A blank space can be used to select elements that are within (descendant) a specific block.
.container .col {
  padding: .5rem 1rem;
}

Above we are adding padding to all the elements with the class col that are inside an element with the class container

  • Child selector > This one selects immediate children of a specific element. It differs from the blank space combinator because with it you cannot select grandchildren of a given parent.
.container > ul {
  border: 1px solid black;
}

Above we select all the ul elements that are immediate children of the .container block.

  • Adjacent sibling selector + This combinator helps to select the adjacent sibling of a given element. Check the example below.
.item2 + li {
  font-size: 2rem;
}

Here we are applying a font size of 2rem to the li that is the immediate sibling of the .item2 element. Note that this and the next combinator only look down the tree.

  • General sibling selector ~ This combinator gives you all the siblings of a given element.
.item2 ~ li {
  color: red;
}

Here we are selecting all the li elements that are sibling of the .item2 element, again, looking down the tree.

And that's it for now! I hope this quick review helped you somehow :)

PS.: this is a living document, if you have any comments, tips or know about missing selectors, let me know in the comments and I will update it.

Discussion

pic
Editor guide
Collapse
scooperdev profile image
Stephen Cooper

Thanks for sharing this! So many, "I didn't realise you could do that moments"

Collapse
willb profile image
willb

Great overview. Every time I dabble with CSS I get stuck with the selectors. This is exactly what I needed.

Collapse
willamesoares profile image
Will Soares Author

Yeah, I know how that feels. I'm happy this post helps you :)

Collapse
animeshmdeshpande profile image
Animesh-M-Deshpande

Thank you, this will be my goto page for CSS selector..