Register to get access to 15+ free programming courses with interactive exercises

Selectors Content layout fundamentals

In previous lessons and exercises, we used fairly simple selectors. They allowed you to select the elements to which the CSS rules would be applied. You already know how to select an element by its tag, class, and identifier, and find a nested element.

CSS gives you much more flexibility when choosing elements. In this lesson, we'll study the most popular second and third selectors in the W3C specification. At the end of the lesson, there'll be links to specifications where you can learn about other selectors.

Selecting an adjacent element

Previously, selectors were used to tell us exactly which element we wanted to select and where it was relative to its parent blocks. In most cases, this is enough to accurately specify an element, but there are cases where you want to select an adjacent element rather than a nested one. For example,

<section>
  <div class="time">...</div>
  <div class="timer">...</div>
</section>

The .timer element is completely dependent on the .time block. Suppose the timer style changes depending on the presence of an element with the .time class. There are several ways to solve such a problem:

  1. Put both elements in a single parent and give them unique classes for different situations
  2. Use the adjacent element selector

More often than not, it is the first option that is preferable. However, you will surely encounter situations where this is not possible. This can happen when dynamically adding elements to the page. And that's where the selectors come in.

There are two selectors in CSS for selecting an element next to another element:

  • A + B means selecting element B, which is immediately after element A. This selector is called the adjacent selector
  • A ~ B means selecting element B, which is on the same nesting level as A. They have a common parent, and all elements of B come after element A in the HTML. Such a selector is called a general sibling selector

In the example above, the adjacent selector is perfect. The .time and .timer elements come one after the other and are children of the same parent. Styling the .timer element depending on the existence of the .time element:

A sibling selector allows you to complicate things a bit. After all, now you can not only select an adjacent element but any element that's on the same level. Let's change the example to clearly demonstrate what a sibling selector can do.

<section>
  <h2>...</h2>
  <div class="time">...</div>
  <h3>...</h3>
  <div class="timer">...</div>
</section>

Schematically, the CSS will look like this:

.time ~ .timer {
  /* styles */
}

Attribute selectors

Attributes are an invariable part of the work of both markup and front-end developers. They don't always carry semantic meaning, such as attributes like alt, title, class, id, and so on.

HTML allows you to add any custom attributes to work with. Custom attributes are those we define ourselves. They don't exist in the documentation or specifications: developers need them to more easily refer to elements or separate similar elements. There can be all sorts of purposes for them and, as you gain experience, especially with JavaScript, you'll notice that custom attributes are not as rarely used as they may seem at first.

There are special selectors in CSS for styling such elements.

The simplest attribute selector simply selects an element by its attribute. In CSS, attributes are written inside square brackets, this is the simplest attribute selector. For demonstration purposes, let's create our own custom attribute, data-full-screen. You can give it a completely different name if you want, since this is just a practice exercise, the actions themselves won't really change.

<section data-full-screen></section>
section[data-full-screen] {
  width: 100vw;
  height: 100vh;

  background: #2196f3;
}

It's also possible to select not just by the name of the attribute, but by its value too. In this case, the attribute name is followed by its value like so:

<section data-full-screen="true"></section>
section[data-full-screen="true"] {
  width: 100vw;
  height: 100vh;

  background: #2196f3;
}

With experience, you'll notice that many JavaScript libraries work specifically with custom attributes. This allows for components to be isolated and reused easily.

There are situations when there is a group of elements in HTML with the same attribute names, but with different values. And some of them may be similar to each other, making up parts of the same component. For example,

<section data-nm-section="catalog"></section>
<section data-nm-section="catalog-popular"></section>
<section data-nm-section="catalog-new"></section>

All three sections, each with their own logic, will have a similar design. You can add the same class to everything, but there's one problem; if elements are added dynamically using JS, then there is a probability that the same class already exists within the project.

This will cause a collision when one selector overrides the properties of another. That's why attributes are used.

All three sections have the same prefix, catalog. This helps separate them from the other section names using the [data-nm-section^="catalog"] construct. This selector will select all elements with the attribute data-nm-section and whose value begins with catalog.

[data-nm-section^="catalog"] {
  width: 50px;
  height: 50px;

  margin-bottom: 10px;

  background: #2196f3;
}

Several other similar constructs look for the occurrence of a substring within a string:

  • [data-nm-section$="catalog"] - looking for the occurrence of the substring at the end of the attribute value
  • [data-nm-section*="catalog"] - looking for the occurrence of the substring anywhere in the attribute value

The expression "occurrence of a substring in a string" should be understood as "string b is part of string a". For example, take the phrase "The quick brown fox jumped over the lazy dog" and "lazy dog". The phrase "lazy dog" is part of the sentence "The quick brown fox jumped over the lazy dog," so the substring is said to be part of the string.


Do it yourself

Take any of the previous lessons and, using selectors by attribute and selectors by the adjacent element, remake the layout.


Recommended materials

  1. Selectors in W3C specification

Аватары экспертов Хекслета

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending the form, you agree to Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending the form, you agree to Personal Policy and Service Conditions

Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.