Using CSS selectors in CSS and JS
Edit me

Overview

CSS selectors are selectors used to select the HTML elements in the DOM tree. These CSS selectors are used in CSS to provide styling to the selected elements, but can also be used in JavaScript to select an element in the DOM using querySelector().

Types of CSS selectors

There are a few types of ways to select an element. Let’s see all the types:

Tag/Type selector

HTML tags can be selected using their tag name.
Example: the HTML tag <h3> can be selected using h3.

ID selector

Selects an element based on the value of its id attribute.
Syntax: #idname
Example: #toc will match the element that has the ID “toc”.

Class selector

Selects all elements that have the given class attribute.
Syntax: .classname
Example: .index will match any element that has a class of “index”.

Attribute selectors

HTML elements can have any attribute. We can also use these attributes and the value of the attributes to select an element.


Let’s consider the following HTML:

<h3 class="Heading custom-red">Heading</h3>
<p class="custom-sub-para">para</p>
<div>Div</div>

[attr]

By just mentioning the type of attribute as per the syntax, we can select all the HTML elements that have that attribute irrespective of the attribute’s value.
Example: [class] will select both the heading and the para above.

[attr=value]

Represents elements with an attribute name of attr whose value is exactly value.
Example: [class="custom-sub-para"] will only select the para.

[attr~=value]

Represents elements with an attribute name of attr whose value is a whitespace-separated list of words, one of which is exactly value. Example: [class~="custom-red"] will only select the heading as that class is a separate word from heading.

[attr^=value]

Represents elements whose attribute’s values start with value. This doesn’t need to be a full word. Example: [class^="cust"] will only select the para as that class value starts with “cust”.

[attr$=value]

Represents elements whose attribute’s values end with value. This doesn’t need to be a full word. Example: [class$="-red"] will only select the heading as that class value ends with “-red”.

[attr*=value]

Represents elements whose attribute’s values contains value anywhere. This doesn’t need to be a full word. This may be the most used type of attribute selector. Example: [class*="custom"] will select both heading and para as they both have a mention of “custom” in its class value.

case insensitive

For selecting elements irrespective of elements attribute value being in uppercase or lowercase, you can use add an i or I after the quotes but before the closing square bracket. Example: [class*="heading" i] will select the heading even though its class starts with “Heading” due to the case insensitve property.

Direct Children selectors

To select only the direct children of an element, we can use the combinator > between 2 CSS selectors for parent and child.
Example: A > B will select element B inside element A where A represents selector of parent and B represents selector of child.

Descendant selectors

To select elements that may be direct child or child of a child of a child element, we can simply add a space between the selectors for parent and child. Example: div p will select all paragraphs within div tags.

Next Sibling selector

The + combinator matches the second element only if it immediately follows the first element. Example: h2 + p will match all <p> elements that immediately follows <h2> element.

Following Siblings selector

The ~ combinator helps select all the matched siblings of an elemnent that come after that first element. Don’t necessarily have to be the next immediate sibling. Example: h2 ~ p will select all the paragraphs that come after the h2 heading if they both share the same parent element.

Universal selector

The universal selector * selects all the elements in the document and you might have used it on top of your css file like,

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

But the most important use for this selector is when it is used together with other types of selectors. For example:

  1. section * will select all the descendants of section.
  2. #custom-form > * will select all the direct children of #custom-form.
  3. .is-active + * will select whatever is the next element sibling of .is-active, if there is any.
  4. [data-title*="custom"] ~ * will select all the element siblings that come after [data-title=”custom”]*.
  5. ul > *:not(li) will select all the children of ul that are not li.

More Specific selectors

Specificity is the weight of a given CSS selector. This weight gives priority to the different CSS selectors. CSS selectors with higher prirority or weight override other CSS styles. When multiple CSS selector declaration have equal specificity, the last declaration found in the CSS is applied to the element. Specificity only applies when the same element is targeted by multiple CSS selector declarations. You can check the order of specifity here.

How to make your selectors more specific?

  1. As per CSS rules, directly targeting an element like li will have more precedence over the styles that the li might inherit from its ancestors like ul. <div markdown="span" class="alert alert-info" role="alert"> Note: Universal selector has the lowest specificity.</div>

  2. IDs are more specific than classes or attributes, which are more specific than tag name selectors.
  3. You can combine multiple selectors of the same element. You can do this by writing both the selectors A and B without a space in between in the declaration like AB. For example, <li id="example" class="active"> has selectors li, #example, and .active. It’s selector (though not necessary with ID) can be combined as li#example.active. Multiple class selectors can also be combined to make it more specific. <div markdown="span" class="alert alert-success" role="alert"> Tip: Space between selectors A B means select all B inside A and no space like AB means the same element.</div>
  4. Adding more selectors for parent or other ancestors can make the selector more specific. For example, section ul > li is more specific than ul > li and li.
  5. Inline styles are more specific than Internal styles which are more specific than External styles.

The “!important” exception

This is not recommended. If your styles are being overridden by other CSS styles with more specificity, rather than using !important, try to add more specificity to your CSS selectors. You can check the order of specifity here. You might have to use this exception in the following cases:

  1. Inline CSS can only be overriden using !important.
  2. CSS styles which already have an !important can only be overriden with !important but in a more specific CSS selector.

Read more about the exception.

Psuedo Classes

Psuedo classes are applied together with other selectors using :. It selects elements from DOM based on the state information. For example, while input will select all the inputs regardless, input:checked will only select the inputs that are checked. It will not apply styles to any input that isn’t checked. However, notice that the styles being applied using Psuedo Classes are still applied to the element input.

Check all the psuedo-classes avaialable here.

Psuedo Elements

Psuedo Elements use :: to select elements that don’t exist as elements themselves in the DOM. For example, in section::before unlike using Psuedo Classes, the styles are not applied to the section but to an imaginary element represented as ::before inside the section. That’s the main difference between Psuedo Elements and Psuedo Classes.

Check all the psuedo element selectors available here.

Grouping Selectors

2 or multiple selectors can be grouped together inside 1 selector by simply adding a , in between them. For example, a selector A and selector B can be grouped together like A, B which will match all the elements that are matched by either A or B. This can be used for applying same styles to different elements with different selectors.

.custom-button, #background-div {
    color: white;
    background: black;
}

Selecting elements in JavaScript

CSS selectors is useful in JavaScript as well. Instead of using document.getElementByID('idname') or other methods, we can simply reuse the CSS selectors we have found for styling in JavaScript for selecting elements and vice-versa using the following methods:

querySelector()

document.querySelector('#idname') will return us the first element that matches this CSS selector in the document. If no matches are found, null is returned. So this method either returns us an element if found, or null. These values after type-conversion can tell us in Boolean if an element exists or not. For example,

var count = document.querySelector('#idname') ? 'Hello' : 'World';

Value of count will be “Hello” or “World” depending upon if the #idname element exists or not.
document.querySelector('form input:checked') will return us the input only if the input is checked and can thus help check conditions where we want to figure out if input is checked or not.
Hence, this method can also be used to check if CSS selectors you want to use are correct or not, by checking if they are selecting the right element.\

This method is applied on an element and searches for the match of the given CSS selector inside that element. The given element doesn’t need to be the document element only. Let’s see the following example:

var ele = document.querySelector('#idname');
var descendant = ele.querySelector('.class');

Assuming #idname element exists, the value of variable ele will be this element. Now in the second line of code, we are searching for selector .class inside the element ele. Similarly, descendant value will be an element if that element exists inside #idname or null if it doesn’t.

var selector = '#idname';
var ele = document.querySelector(selector);

We pass a string as argument inside the method. We can save the string as a variable and directly pass the variable in the method instead.

querySelectorAll()

As the name suggests, unlike querySelector() where only the first element is returned, querySelectorAll() returns us an HTML Collection (like an Array) of all the elements that match that selector. If no element matches the selector, it returns us an empty HTML Collection, with length = 0. Other than that, they are the same as they both are applied on elements you want to search in, and both take in an argument of type string. Elements of the HTML collection returned can be selected using the index in bracket notation (same as Arrays).

Dynamic Selectors

Here at OptiPhoenix, you might come across sites like harrods.com that were built using frameworks. These frameworks or other tools create the HTML with custom randomly generated classes. This can be seen in the following HTML snippet from one of the sites.

<div class="weather-wrapper e1wib5c13 css-5griwq egmhfll0">
    <div class="css-1241pyg e1wib5c11">
        <div data-test="weatherCard" class="css-igd5xo e1vbzrz90">
            <h2 data-test="weatherCard-title" data-heading="weatherCard-176o364" class="css-176o364 e1vbzrz91">
                Weather
            </h2>
            <div data-test="weatherCard-degrees" class="css-1hvu4b9 e1vbzrz92">6º</div>
            <div data-test="weatherCard-status" class="css-1hgz0vd e1vbzrz93">Light Cloud</div>
            <div data-test="weatherCard-provider" class="css-1ceztl5 e1vbzrz94">BBC Weather</div>
            <p class="css-ajbq9h e1vbzrz95">Monday 20 December 11am - 7pm</p>
            <div data-test="openingHoursWidget-wrapper" class="css-oqktz2 eh50f7b0">
                <ul data-test="openingHoursWidget-item" class="css-1wee9yr eh50f7b1">
                    <li data-test="openingHoursWidget-item">
                        <p class="css-in94gg e4n9ems0">Monday 20 December - Wednesday 22 December: 11am - 7pm </p>
                    </li>
                    <li data-test="openingHoursWidget-item">
                        <p class="css-in94gg e4n9ems0">Thursday 23 December: 11am - 8pm </p>
                    </li>
                </ul>
                <a class="css-1kfzfam e4n9ems1" data-test="openingHoursWidget-link" href="/en-gb/plan-your-visit">
                    View all opening hours
                </a>
            </div>
        </div>
        <div data-test="storeInformation" class="css-1n442qm e1diceo40">
            <a class="css-lqs3il e1diceo41" data-test="storeInformation-title" href="/en-gb/plan-your-visit">
                Today at Harrods
            </a>
            <blockquote data-test="storeInformation-quote" class="css-1hrcaty e1diceo43" style="">
                Discover our Knightsbridge store, home to over 5,000 brands and seven floors of luxury.
            </blockquote>
            <a class="css-anm21b e1diceo44" data-test="storeInformation-link" href="/en-gb/plan-your-visit">
                Plan Your Visit
            </a>
        </div>
    </div>
</div>

As it can be seen above, the classes here are generated dynamically by a computer. These classes exist now with this name, but after new changes by client in the site, these classes will be generated again and may be different than before. Hence these classes are not reliable selectors for these sites.

How do we select these dynamic elements?

Even though most of the classes are dynamic, the tag names, some attributes, the parent-child tree, and even some classes remain static. We can use the other types of selectors we have learned above. For example, let’s checkout the HTML given above.

  1. .weather-wrapper seems like a static class and not computer generated. \
  2. .weather-wrapper a will select all the anchor tags inside it. \
  3. .weather-wrapper ul + a will only select the anchor tag next to the ul. \
  4. .weather-wrapper [data-test="weatherCard-title"] , .weather-wrapper h2, .weather-wrapper [data-heading*="weatherCard"] will all select the same heading tag.
  5. In the h2 tag, there exists a data-heading=”weatherCard-176o364” attribute. This attribute has number in it and that number of it might be dynamic but the text might be static and can be used using this property.
  6. We can traverse the whole parent like .weather-wrapper > div > div > a ~ a. This will select only the Plan your visit anchor tag.
Tags: