CSS: Селекторы. Знакомство

Ранее вы познакомились с тремя базовыми селекторами - по тегу, классу и идентификатору.
В этом уроке рассмотрим селекторы чуть глубже.
Универсальный селектор
Символ * - универсальный селектор для CSS. Он соответствует любому тегу, но не включает псевдоэлементы.
Убирая звёздочки с простых селекторов имеет тот же эффект. Например, * .warning и .warning считаются равными.

Но есть причина использовать этот селектор пореже. Он имеет нулевую специфичность, а значит, может быть перекрыт любым селектором класса, элемента или id-селектором.
* 
{ 
   color:red; 
}
Делает ВСЕ элементы HTML красными.
Группировка селекторов
Иногда может возникнуть следующая ситуация: вам нужно сделать одно и то же с разными тегами, например, покрасить все заголовки h2, h3 и абзацы в красный цвет.
Вы можете написать так:
h2 
{
   color: red;
}

h3 
{
   color: red;
}

p 
{
   color: red;
}
Однако, существует способ сделать это немного короче: селекторы тегов можно объединять через запятую и CSS код применится к ним всем одновременно.
h2, h3, p 
{
   color: red;
}
Бывает и такое, что часть свойств некоторых селекторов одинакова, а часть - отличается.
В следующем коде, например, заголовки и абзац имеют красный цвет, но у абзаца кроме этого есть еще свойства:
h2 
{
   color: red;
}

h3 
{
   color: red;
}

p 
{
   color: red;
   text-align: justify;
   font-size: 16px;
}
В таком случае можно сгруппировать повторяющиеся части, а то, что не повторяется, записать отдельно:
h2, h3, p 
{
   color: red;
}

p 
{
   text-align: justify;
   font-size: 16px;
}
Примечание
Группировать через запятую можно любое количество селекторов тегов, пробел после запятой не имеет значения, можно без него, но с ним код более красивый.
Сочетание селекторов по классу и тегу
Синтаксис CSS позволяет выбирать элементы не только по одному классу или тегу.
Можно, например, выбрать элемент одновременно по тегу и по классу или же элемент с двумя классами сразу. Для этого селектор составляется просто одной строкой из всех желаемых «частей» без пробелов. Давайте рассмотрим примеры.

В селекторе по тегу и классу первым пишется название тега, а потом идёт класс:
Пример 1
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Пример</title>
 </head>
 <body> 
  <ul class="target">
    <li>список</li>
  </ul>
 </body>
</html>
/* выбор всех тегов ul с классом target */
ul.target 
{
   ...
} 
Если у элемента задано несколько классов, в HTML и в CSS-селекторе они могут идти в разном порядке — это не будет влиять на выборку элементов:
Пример 2
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <span class="text green">Зеленый текст</span>
    <p class="green text">Тоже зеленый текст</p>
  </body>
</html>
/* выбор элементов с двумя классами: text и green */
.text.green 
{
   color:green;
}
Четыре вида взаимоотношений в дереве элементов
Сначала кратко познакомимся с деревом элементов.
HTML-документ — это как большое семейное древо, с родителями, братьями, детьми, предками и потомками.
Это дерево поможет проследить и выяснить как CSS видит взаимодействие элементов на странице.

Предок - html-элемент, который заключает в себе все другие элементы.
Пример, body предок для h1, p, span, ul, li - всех, содержащихся в нем элементов.

Потомок - элемент, который расположен внутри одного или более тегов.
Проще говоря, потомки - любые элементы, расположенные внутри родительского элемента
Пример, body потомок html, а h1, p, ul - потомки body и html одновременно.

Родитель - элемент, который связан с другими элементами более низкого уровня и находится выше на один уровень.
Пример, html - родитель только для head и body. ul - родитель li. p - родитель для span.

Ребенок или дочерний элемент - просто прямой потомок. Подчинен другому элементу более высокого уровня, сам находится на уровень ниже родительского.
Пример, h1 и p - дочерние по отношению к body.
head и body - дочерние по отношению к html.

Соседний или братский элемент - дочерние элементы одного родителя, расположенные на одном уровне.
Пример, h1, p, ul - соседние элементы от одного родителя body.
head и body - соседние элементы от родителя html.
Заметьте, что title, h1, p и ul - не являются соседями, т.к. у них разные родительские теги.
1. Контекстные селекторы (родители-потомки)
Этот селектор встречается в практике наиболее часто. С помощью его можно определить область влияния стилей. Он даёт нам гарантию, что стиль будет применён к необходимому нам элементу.

Контекстные селекторы - это перечисленные через пробел селекторы, которые обозначают вложенность от родительских элементов к потомку. Эта связь позволяет управлять стилями вложенных элементов.
Синтаксис
x y
{
   свойство1: значение;
   свойство2: значение;
}

/* x — селектор-родитель, y — селектор-потомок
Правило будет действовать на селектор y */
При чтении контекстного селектора первая часть указывает на родительский элемент, а последующие — на вложенные.

Ключевой момент - селектор выбирает всех потомков не зависимо от их уровня вложенности.

Рассмотрим на примере.
Есть 2 списка. Надо покрасить маркированые списки в красный, а нумерованные в зеленый.
Пример 3
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <ul>
      <li>пункт</li>
      <li>пункт</li>
    </ul>
    <ol>
      <li>пункт</li>
      <li>пункт</li>
    </ol>
  </body>
</html>
Это можно сделать с помощью контекстного селектора.
Для этого нужно указать селектор родителя, а через пробел - селектор потомка.
В нашем случае селекторы ul и ol - родители, а li - потомоки по отношению к ul.
Покрасим их в нужные цвета:
/*Означает, что ul li выберет все теги li из списка ul, 
а селектор ol li - выберет все теги li из списка ol.*/

ul li 
{
   color: red;
}

ol li
{
   color: green;
}
Тоже самое правило работает и для классов.
Усложним пример, добавим в него классы и один элемент списка сделаем жирным, а второй курсивным и жирным. Сделаем так для того, чтобы наглядно убедиться, что селектор родителя выберет все вложенные в него элементы.

Также обратите внимание, что вложенный селектор не обязательно должен состоять из двух селекторов тегов или классов - их может быть любое количество, записанное через пробел.
Пример 4
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <ul class='red'>
      <li><b>жирный пункт</b></li>
      <li><b><i>курсивный жирный пункт</i></b></li>
    </ul>
    <ol class='green'>
      <li>пункт</li>
      <li>пункт</li>
    </ol>
  </body>
</html>
/* выбрать все элементы с классом .big внутри элементов li, 
    которые лежат внутри элементов с классом .red */

.red li .big
{
   color: red;
}

/* выбрать все элементы b внутри элементов li, 
    которые лежат внутри элементов с классом .red */
/* обратите внимание на то, что внутри есть еще один элемент - i, 
    но он тоже "вложился"  в своего предка*/
.red li b
{
   color: blue;
}


.green li
{
    color: green;
}
Частая ошибка новичков
Контекстные селекторы часто могут стать селектором тегов с классом из-за лишнего пробела.
Например:
Пример 5
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Пример</title>
 </head>
 <body> 
  <p class="colored_text">Все любят арбузы</p>
  <p class="colored_text">Как думате арбуз - ягода, фрукт или овощ?</p>
 </body>
</html>
/* Выбираем абзацы с классом*/
p.colored_text
{
   color:green;
}
А вот как лишний пробел его меняет:
p .colored_text
{
   color:green;
}
Это селектор отличается от предыдущего тем, что у него между именем тега и именем класса стоит пробел. Этот пробел превращает его в селектор потомков. То есть в данном случае мы выбираем все элементы с классом colored_text, находящиеся внутри абзацев. И так как сейчас выбирать нечего, то в примере ничего не произошло.

Но если мы добавим потомков для абзаца, то все сработает.
Пример 6
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Пример</title>
 </head>
 <body> 
  <p><span class="colored_text">Все любят арбузы</span></p>
  <p><span class="colored_text">Как думате арбуз - ягода, фрукт или овощ?</span></p>
 </body>
</html>
p .colored_text
{
   color:green;
}
Примечание
Итак, еще раз: p.colored_text - такой селектор выбирает абзацы с классом colored_text.
А вот так: p .colored_text, то выберу все элементы с классом colored_text, находящиеся внутри абзацев.
Прочувствуйте эту разницу.
2. Соседние селекторы (братья)
Контекстные селекторы используются для вложенных друг в друга элементов, а соседние — для расположенных рядом, то есть они следуют непосредственно друг за другом в коде документа.
Соседние селекторы записываются с помощью знака "+".
Синтаксис
x + y
{
   свойство1: значение;
   свойство2: значение;
}

/* стиль применяется для соседнего селектора y */
Стили применятся к элементу, подходящему под y, только если сразу перед ним расположен элемент, подходящий под x.
Поясним:
Пример 6
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <p>Стиль при такой <b>записи</b> применяется к <i>элементу</i> Y.</p>
    <p>Но только в том случае, если он <i>является</i> соседним для элемента X и следует сразу после него.</p>
  </body>
</html>
b + i
{
   color: red;
}
Стиль описанный в примере будет применен только к первому тексту заключенному в тег <i>, т.к. он следует сразу же после тега <b>.
В примере теги i и b - соседние селекторы. То, что они расположены внутри элемента p, никак не влияет на их отношение.

Рассмотрим еще один пример, на этот раз с классами.
Пример 7
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <ul>
      <li class="hit">пункт</li>
      <li class="miss">пункт</li>
      <li class="hit2">пункт</li>
    </ul>
  </body>
</html>
.hit + .miss
{
   color: red;
}

.miss + .hit2 
{
   color:green;
}
Селектор .hit + .miss применил стили к элементу с классом miss, так как перед ним есть элемент с классом hit.
Селектор .miss + hit2 применил стили к элементу с классом hit2, т.к. перед ним есть элемент с классом .miss.

А вот селектор .miss + .hit не сработает, так как элемент с классом miss находится после элемента с классом hit в разметке.

На заметку. Их можно комбинировать с контекстными селекторами.
3. Родственные селекторы
Родственные селекторы (по своей сути тоже братья) позволяют выбирать элементы HTML-страницы, являющиеся родственными по отношению друг к другу (то есть имеющие общего родителя и находящиеся на одном уровне).

Данные селекторы похожи на соседние, но отличаются тем, что стиль применяется ко всем элементам выбранного типа, а не только к первому из них. Вместо знака + используется символ тильда ~.
Синтаксис
x ~ y
{
   свойство1: значение;
   свойство2: значение;
}

/* x — родитель, y — ребенок
Правило будет действовать на селектор y */
Пример 8
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <div>
        <p>Текст</p>
        <h1>Заголовок 1</h1>
        <p>Текст</p>
        <p>Текст</p>
    </div>
    <div>       
        <h2>Заголовок 2</h2>
        <p>Текст</p>
    </div>
    <div>
        <p>Текст</p>
    </div>
  </body>
</html>
h1 ~ p 
{
   font-style: italic; /* Наклонный шрифт для всех абзацев */
   color: red;
}

h2 ~ p 
{
   color:blue;    
}
В примере стиль применился ко всем тегам <p>, которые следуют после тега <h1> и находятся до закрывающего тега родителя <div>.
Тоже самое для тега <p>, который следует после тега <h2>.
4. Дочерние селекторы
Дочерние селекторы похожи на контекстные. Основное отличие — контекстный селектор (потомка) выбирает всех потомков, независимо от уровня вложенности, а селектор дочернего элемента выбирает потомков только первого уровня — то есть непосредственно вложенные элементы.

Чтобы указать прямую связь с дочерним элементом, существует специальный символ ">".
Например: ul > li или ul > li > b.
Синтаксис
x > y
{
   свойство1: значение;
   свойство2: значение;
}

/* x — родитель, y — ребенок
Правило будет действовать на дочерний селектор y*/
Чтобы лучше понять, как пользоваться дочерними селекторами, давайте сравним их с контекстными селекторами селкторами потомков).
Пример 9
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <ul>
      <li><i>курсивный пункт</i></li>
      <li><b><i>курсивный жирный пункт</i></b></li>
      <li><i>курсивный пункт</i></li>
    </ul>
  </body>
</html>
/* Контекстный селектор*/
/* выбраны все курсивы внутри пунктов списка*/
li i
{
   color: red;
}

/* Дочерний селектор*/
/* выбраны только курсивы, являющиеся непосредственными потомками абзацев*/
li > i
{
   color: red;
}
По отношению к списку <ul> элементы <li> являются дочерними элементами и потомками, а <i> и <b> — только потомки.

В примере выше тег <i>, в первом пункте списка вложен непосредственно в <li>, а во втором он вложен в тег <b>, хотя также является потомком тега <li>. Поэтому во втором пункте списка CSS правило для дочернего селектора li > i { color: red; } не сработает – наклонный текст не будет отображён красным цветом.
Псевдоклассы
Следующий набор селекторов относится к псевдоклассам и псевдоэлементам. Их очень много, и они часто служат довольно специфическим целям, поэтому мы рассмотрим лишь некоторые, часто используемые.

Псевдоклассы — это дополнения к обычным селекторам, которые делают их ещё точнее и мощнее.
Псевдокласс выбирает элементы, находящиеся в специфическом состоянии, например, они являются первым элементом своего типа, или на них наведён указатель мыши.
В аналогии с оружием, обычный селектор — это снайперский прицел, а с псевдоклассом он дополняется прибором ночного видения.

Псевдокласс добавляется к селектору c помощью символа двоеточия :
Синтаксис
селектор: псевдокласс 
{ 
   свойство: значение; 
}
Давайте рассмотрим простой пример. Если бы мы хотели сделать шрифт первого абзаца статьи более крупным и жирным, мы могли бы добавить класс к этому абзацу, а затем добавить CSS к этому классу, как показано в 10 примере ниже:
Пример 10
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
   <body> 
      <article>
         <p class="first">Арбуз<br> Название «арбуз» происходит от 
         персидского и тюркского слова arbuza – дыня (дословный перевод «ослиный огурец»). 
         Родиной считается Африка.</p>

         <p>Арбуз относится к семейству тыквенных, 
         и является близким родственником огурца, дыни, кабачка и тыквы.</p>
      </article>
  </body>
</html>
.first 
{
   font-size: 120%;
   font-weight: bold;
}   
Однако поддержка может оказаться утомительной — что если новый абзац будет добавлен в верхнюю часть документа? Тогда нам нужно будет передвинуть класс к новому абзацу.

Вместо добавления класса можно использовать селектор псевдокласса :first-child — он всегда будет выбирать первый дочерний элемент своего родителя, и нам больше не нужно будет редактировать HTML.
article p:first-child 
{
   font-size: 120%;
   font-weight: bold;
}   
А теперь зададим те же свойства, только для последнего элемента.
В этом поможет псевдокласс :last-child находит любой элемент, являющийся последним в его родителе.
article p:last-child 
{
   font-size: 120%;
   font-weight: bold;
}   
Все псевдоклассы ведут себя подобным образом. Они нацелены на какой-то фрагмент вашего документа, находящийся в определённом состоянии, и ведут себя так, как если бы вы добавили класс в свой HTML.
Примечание
Правильно писать псевдоклассы и элементы без какого бы то ни было предшествующего им селектора элемента.
В примере выше можно было написать :first-child и правило было бы применено к любому элементу, оказавшемуся первым дочерним для <article>, не только к первому дочернему абзацу — :first-child равнозначно *:first-child.
Однако обычно верстальщикам хотчется большего контроля, поэтому используют более специфичный селектор.
Псевдоклассы пользовательского действия

Некоторые псевдоклассы применяются только тогда, когда пользователь некоторым образом взаимодействует с документом. Эти псевдоклассы, иногда называемые динамическими псевдоклассами, действуют так, как если бы класс был добавлен к элементу в момент взаимодействия с ним пользователя.

  • :hover - применяется только в том случае, если пользователь наводит указатель мыши на элемент, обычно на ссылку.
  • :focus - применяется только в том случае, если пользователь фокусируется на элементе, используя управление с клавиатуры.
  • :link - выбирает ещё не посещённые ссылки.
  • :visited - выбирает посещённые ссылки.
  • :active - выбирает активные ссылки (кнопка мыши зажата на ссылке).

Чтобы правильно стилизовать ссылки, правила для :hover прописывается сразу после :link и :visited правил, но идет перед :active. Если их расположить по-другому, то некоторые могут не сработать.
Следуйте правилу LVHA: :link — :visited — :hover — :active и все будет работать правильно, при условии, что вы используете все эти псевдоклассы в коде.
Пример 11
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Пример</title>
  </head>
  <body> 
    <p><a href="">Наведись на меня мышкой</a></p>
  </body>
</html>
a:link,
a:visited 
{
   color: purple;
   font-weight: bold;
}

a:hover 
{
   color: hotpink;
} 
Псевдокласс :nth-child

Псевдокласс :nth-child используется для добавления стиля к элементам на основе нумерации в родительском элементе.
С помощью псевдокласса nth-child можно выбирать теги по порядковому номеру, не используя классы.
Синтаксис
селектор:nth-child (odd | even | <число> | <выражение>) 
{
   ...
}

Значения
odd - Выбирает все нечетные номера элементов.
even - Выбирает все четные номера элементов.
число - Порядковый номер дочернего элемента относительно своего родителя. Нумерация начинается с 1, это будет первый элемент в списке.
выражение - Задается в виде an+b, где a и b целые числа, а n — счетчик, который автоматически принимает значение 0, 1, 2...
В значении может быть указан как порядковый номер элемента:
li:nth-child(2) { ... }
li:nth-child(4) { ... }
Первый селектор выберет второй элемент списка, второй селектор — четвёртый элемент списка.

Так же можно задать выражение, которое задается в виде an+b, где a и b целые числа, а n — счетчик, который автоматически принимает значение 0, 1, 2 и т.д.
С помощью выражения можно задать сдвиг вперед или назад: (3n+1) — найдет каждый третий и сделает один шаг вперед, а (3n-2) — два шага назад от найденого.

Если размер цикла не задан — (n) — выберутся все элементы списка.

Примеры:
Псевдокласс :nth-last-child работает абсолютно аналогично, только счёт ведётся с конца.
Примечание
По возможности, старайтесь избегать сложных выражений с nth-child Для более сложных выборок на странице старайтесь все же прописывать классы напрямую в HTML, а не работать с запутанными выражениями nth-child.

Выражения типа li:nth-child(-n+3) или li:nth-child(5n+1):not(nth-child(3n-1)) слишком сложны для понимания. Думаю, вы скажете:"Понятия не имею, что они значат, но они работают!"

К сожалению, в некоторых сложных дизайнах без таких выражений не обойтись. Но представьте, что вы вернетесь к коду через пару месяцев и попытаетесь понять, что вы хотели сделать. Даже у опытных экспертов по CSS на это уйдет довольно много времени.
Полезные ссылки
Интересный тренажер по селекторам - https://flukeout.github.io/
Нагядное применение псевдокласса nth-child - https://css-tricks.com/examples/nth-child-tester/
Статья про псевдокласс nth-child - https://snipp.ru/html-css/nth-child

Практика
1. Визуально проще
Упрости код, используя группировку селекторов.
h1 
{
   text-align: center;
   color: red;
}

h2 
{
   text-align: center;
   color: red;
}

h3 
{
   text-align: center;
   font-size: 16px;
   color: red;
}
2. Повтори страницу
Напишите HTML-страницу с заголовком первого уровня и любым текстом.
С помощью внешних таблиц стилей подключите к HTML файл style.css.

Задайте заголовку с помощью селектора по тегу:
  • шрифт Arial
  • размер 72px
  • цвет текста #A64100
  • выравнивание по центру
  • цвет фона #FFCB73
А тексту:
  • шрифт Times New Roman
  • размер 24px
  • цвет текста #BF6830

У нас получилось так, а у вас? Результатом пришли скриншот наставнику.
Предыдущее занятие | Следующее занятие