Adding a class or ID isn’t the only way to style some parts of your document differently. Our more complex selectors give you ways to select elements based on where they are in the document. The first set of selectors we can look at are the Combinators. There are four different Combinators and they work by combining other selectors.

The CSS Layout Workshop: Combinators

Descendant Selector

Select all elements that are descendants of a specified parent.

When you use a descendant selector you can specify a parent selector and then the element that you wish to style. So if I have markup like this:

<p>Content here</p>
<div class="box">
  <p>Content inside the box class.</p>

If I were to select the p element with an element selector that would style both the paragraphs - the one inside the div with a class of box as well as the one outside. To only style the p inside .box I use a descendant selector.

.box p { }

Here .box is the specified parent and p is the selector inside that parent that we are going to apply CSS styles to.

You can have more than one descendant in the list. For example, only selecting li elements that are children of a ul element inside a parent with a class of box.

.box ul li { }

The above selector would not select an li element if it were inside an ol. With long chains of selectors you again can fall into the trap of making CSS that is very hard to reuse. If your selectors get more than three elements deep that often indicates there would be a better way to structure your CSS.

Child Selector

Select all elements that are immediate children of a specified parent

The descendant selector will target all children, even if there are other elements in-between the parent and child. For example if we take the following markup:

<ul class="items">
  <li>Item One</li>
  <li>Item Two
      <li>Item 2a</li>
      <li>Item 2b</li>
  <li>Item Three</li>

If we used a descendant selector:

.items li { }

This would select all li items, those nested inside the first ul and those inside the ul nested inside the li for item two. If we instead use a child selector:

.items > li { }

This will only select the li elements that are a direct child of .items. If there is another element in-between then the li will not be selected. An example of where you might use this technique is to remove the list bullets from the outer list but keep them for nested lists.

.items > li {
  list-style: none;

Adjacent Sibling

Select elements that are the adjacent siblings of an element

The adjacent sibling selector is a useful selector that enables the selection of an element when it is next to another element. This is how we can deal with things like stacked headings in CSS.

A common request is to make the paragraph that comes directly after a heading different in some way, to make it stand out. That can be achieved with an adjacent sibling, the following selector selects the p element only when it comes after an h2.

h2 + p { }

You can of course combine these. If you only want this to happen for content inside a wrapper with a class of .box you can combine a descendant selector with the adjacent sibling.

.box h2 + p { }

The above selector will only select a p element that comes after an h2 element where both are inside something with a class of .box.

General Sibling

Select elements that are the siblings of an element

The general sibling selector works in a similar way to adjacent sibling but will select all siblings that come after the specified element - even if they are not adjacent to each other. This selector was added in Level 3 of the Selectors module.

If I want to select all p elements that come after an h2 I would use:

h2 ~ p { }

This would select paragraphs but not headings, lists or anything else.

Next lesson: Attribute Selectors