CSS Selectors

# CSS Selectors

In CSS, a selector targets HTML element(s) to apply styles to content. Each CSS rule starts with a selector or a list of selectors. These selectors help the browser have fine-grained precision when selecting elements to style.

# Type, class, ID selectors

# Type selectors

A type selector is as known as a tag name selector or element selector because it selects an HTML tag/element in the document.

/* these are all type selectors */
h1 {
    background-color: blue;
}

h2 {
    color: orange;
}

strong {
    font-weight: bold;
}

span {
    font-family: Serif;
}

The universal selector is indicated by an asterisk (*). It selects everything in the document (or inside the parent element if it is being chained together with another element and a descendant combinator).

The universal selector can be used to make the selectors easier to read. For example, if we want to select any descendant of elements of an <article> element that are the first child of their parent, including direct children, and make them bold, we could use the :first-child pseudo-class.

article :first-child {
    font-weight: bold;
}

However, this elector could be confused with article:first-child, which will select any <article> element that is the first child of another element.

To avoid this confusion, we can add the universal selector to the :first-child pseudo-class, so it is more obvious what the selector is doing.

article *:first-child {
    font-weight: bold;
}

# Class selectors

The class selector starts with a dot . character. It will select everything in the document with that class applied to it.

.highlight {
    background-color: yellow;
}

Targeting element with multiple classes applied:

<div class="text">Some text</div>
<div class="text safe">This text is safe.</div>
<div class="text danger">This text shows danger!</div>
.text {
    color: black;
}

.text.safe {
    color: green;
}

.text.danger {
    color: red;
}

# ID selectors

An ID selector begins with a #. It is used in the same way as a class selector. However, ID should be unique across the page and elements can only have a single id value applied to them.

Avoid using the same ID multiple times in a document. It would result in invalid code and unexpected behaviors.

An ID has high specificity. It will overrule most other selectors. In most cases, it is preferable to add a class to an element instead of an ID.

# Attribute selectors

Attribute selector [attribute]

  • [attribute=value] exact match
  • [attribute^=pre] prefix match
  • [attribute$=post] suffix match
  • [attribute~=substring] substring match

# Selector combinator

  1. Descendent combinator selector selector

    • Use space, i.e. p a will match all <a> elements inside <p> element.
    • let links = document.querySelector("p a");
      
  2. Child combinator selector > selector

    • Finds all elements that are direct children of the first element.
  3. General sibling combinator selector ~ selector

    • Finds siblings that share the same parent.
    • // this will match all <a> elements follows <p>, immediately or not
      let links = document.querySelectorAll("p ~ a");
      
  4. Adjacent sibling combinator selector + selector

    • Matches elements directly follows the preceding element
    • For example, h1 + a matches all <a> elements that directly follow an h1.
    • let links = document.querySelectorAll("h1 + a");
      

# Pseudo-classes and pseudo-elements

# Pseudo-classes

A pseudo-class is a selector that selects elements that are in specific state, e.g they are the first element of their type, or they are being hovered over by the mouse pointer.

The : pseudo matches elements based on their states:

element:state

For example, the li:nth-child(2) selects the second <li> element in a list:

let listItem = document.querySelectorAll("li:nth-child(2)");

Other commonly used pseudo-selectors

  • :nth-last-child(2) count from the last child backwards.
  • :first-child and :last_child first and last child of a parent element.
  • :not(selector) excludes elements matching specified selector.
  • :hover, :active, :focus match based on user interaction.
  • :empty match elements that have no children.
  • :root highest-level parent element, usually <html>.

# Pseudo-elements

Pseudo-elements behave in a similar way. However, they act as if had added a whole new HTML element into the markup, rather than apply a class to existing elements.

The :: represent entities that are not included in the document.

For example, p::first-line matches the first line of all p elements:

let links = document.querySelector("p::first-line");

There are a few special pseudo-elements, when combined with content property, they can insert content into the document using CSS.

The ::before and ::after pseudo-elements in CSS are used for styling elements in a non-destructive way, allowing you to insert content before or after an element's actual content. These pseudo-elements are often used for decorative features, such as adding icons, extra shapes, or even layout elements like clearing floats.

Adding quotation marks to blockquotes

blockquote::before {
    content: "“";
    font-size: 3em;
}

blockquote::after {
    content: "”";
    font-size: 3em;
}

Adding icons before links

.external-link::before {
    content: "↗";
    margin-right: 5px;
}

Adding a clearing element

.clearfix::after {
    content: "";
    display: table;
    clear: both;
}

Limitations

  1. Not actual DOM elements: They are not part of the DOM, so you can't select them or modify them using JavaScript.
  2. Limited styling: Not all CSS properties work with pseudo-elements.
  3. Mandatory content property: They require the content property to be displayed.

# Grouping selectors

  • selector1, selector2, selector 3
  • Use comma to separate, the selector list will match any element with at least one of the selectors in the group.