Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

В предыдущей статье мы разбирались с различными селекторами CSS. В своей работе можете оказаться в ситуации, когда селекторы, относящиеся к одному и тому же элементу, включены в несколько разных правил CSS. Какое же из них "победит", то есть, в конечном счете, будет применено к этому элементу? Это определяется механизмом, который называют Каскадированием, а также связано с наследованием (некоторые, хотя и не все, значения свойств элементы получают от своих родителей). В данной статье дается рассказывается, что представляет собой каскадирование, что значит специфичность и важность, и как происходит наследование от разных правил.

Начальные условия: Базовая литература по компьютерам, наличие основного программного обеспечения (basic software installed), базовые знания о работе с файлами, основы HTML (изучите Введение в HTML) и представление о том, как работает CSS (можно узнать из предыдущих статей данного раздела.)
Цель: Узнать о каскадировании и специфичности, а также о том, как в CSS работает наследование.

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

Каскадирование

CSS - это сокращение от Cascading Style Sheets (Каскадные таблицы стилей), что говорит о том, насколько важным является это понятие. На самом примитивном уровне это указывает на значимость порядка, в котором следуют правил CSS, но все неколько сложнее. Какой из селекторов "победит" зависит от трех факторов (они перечислены соответственно своему весу — идущие впереди перекрывают последующие):

  1. Важность
  2. Специфичность
  3. Исходный порядок

Важность

В CSS есть элемент синтаксиса, позволяющий гарантировать, что определенное правило всегда будет "побеждать": !important.

Рассмотрим пример:

<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>
#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

В результате получаем:

Посмотрим, как это получается.

  1. Как видите, значения color и padding из третьего правила применились, а значение background-color - нет. Почему? Казалось бы, примениться должны все три, поскольку обычно последующие правила перекрывают предыдущие.
  2. Тем не менее, победили первые (в порядке следования) правила, так как селекторы ID/класса более специфичны, чем селекторы элементов (подробнее об этом говорится в следующем разделе.)
  3. Оба элемента имеют класс (class) better, но второй имеет также id со значением winning. Поскольку специфичность ID выше даже чем у класса (ID каждого элемента уникально, тогда как элементов одного класса может быть множество — селекторы ID очень специфичны в этом отношении), ко второму элементу должны были бы примениться красный цвет фона и граница 1 pixel black, а первый первый должен был получить серый фон и не иметь границы, согласно своему классу.
  4. Второй элемент действительно имеет красный фон, но границы у него нет. Почему? Из-за инструкции !important во втором правиле — добавление ее после border: none означает, что это объявление будет иметь приоритет перед значением границы, указанном в другом правиле, не смотря на то, что селектор ID имеет более высокую специфичность.

Примечание: Единственный способ перекрыть инструкцию !important - это включить в исходный код другую инструкцию !important с такой же специфичностью, но позднее.

О существовании директивы !important нужно знать на случай, если она попадется в чужом коде. НО. Мы советуем прибегать к ней только в самом крайнем случае. Например, если вы работаете на CMS, где нельзя редактировать внутренние модули CSS, и вам совершенно необходимо перекрыть значения стилей, которые по-другому никак не перекрыть. Но, серьезно, не используйте ее, если этого можно избежать. Поскольку инструкция !important нарушает нормальное каскадирование, она может серьезно затруднить отладку CSS, особенно в больших таблицах стилей.

Нужно отметить также, что важность (importance) объявления CSS зависит от того, в какой таблице стилей она задана — пользователи могут создавать свои таблицы стилей, перекрывающие таблицы разработчиков. Например, пользователи с плохим зрением могут удвоить размер шрифта для всех посещаемых им веб-страниц.

Конфликтующие объявления применяются в следующем порядке, последующие перекрывают предыдущие:

  1. Объявления в таблицах стилей агента пользователя (например, стили браузера по умолчанию, используемые, когда никаких других стилей не задано).
  2. Обычные объявления в пользовательских таблицах стилей (настраиваемые стили, заданные пользователем).
  3. Обычные объявления в авторских таблицах стилей (это стили, которые задаем мы - веб-разработчики).
  4. Важные (Important) объявления в авторских таблицах стилей
  5. Важные (Important) объявления в пользовательских таблицах стилей

Есть смысл отдавать приоритет таблицам стилей разработчиков перед пользовательскими, чтобы веб-страницы выглядели так, как было задумано, но бывает, что у пользователя есть важные причины для внесения изменения — тогда они могут воспользоваться инструкцией !important.

Специфичность

Специфичность измеряет то, селектор специфичен — то есть скольким элементам он может соответствовать. Как показано в вышеприведенном примере, у селектора элемента специфичностью низкая. У селектора класса она выше, так что он побеждает селектор элемента. Селектор идентификатора (ID) имеет еще более высокую специфичность, и, таким образом, побеждает селектор класса. Единственный способ победить селектор ID состоит в том, чтобы использовать инструкцию !important.

Величину специфичности селектора измеряют согласно четырем разным величинам (или компонентам), которые можно представить как тысячи, сотни, десятки и единицы — четыре цифры в четырех столбцах:

  1. Тысячи: Ставит единицу в этот столбец, если селектор внутри элемента <style> или объявление находится внутри атрибута style (такие объявления не имеют селекторов, и их специфичность всегда равна 1000.) В противном случае ставьте 0.
  2. Сотни: Добавляет единицу в этот столбец за каждый селектор ID, содержащийся внутри составного селектора.
  3. Десятки: Добавляет единицу в этот столбец за каждый селектор класса, атрибута или псевдо-класса, содержащийся в составном селекторе.
  4. Единицы: Добавляет единицу в этот столбец за каждый селектор элемента или псевдо-элемента, содержащийся в составном селекторе.

Примечание: Универсальный селектор(*), комбинаторы (+, >, ~, ' ') и псевдо-класс отрицания (:not) на специфичность не влияют.

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

Селектор Тысячи Сотни Десятки Единицы Итоговая специфичность
h1 0 0 0 1 0001
#important 0 1 0 0 0100
h1 + p::first-letter 0 0 0 3 0003
li > a[href=*"en-US"] > .inline-warning 0 0 2 2 0022
#important div > div > a:hover, внутри элемента <style> 1 1 1 3 1113

Примечание: Если несколько селекторов имеют одну и ту же важность и специфичность, то побеждает тот, что идет позднее в исходном коде (Source order).

Прежде чем продолжить, посмотрим, как это работает. Возьмет следующий код HTML:

<div id="outer" class="container">
  <div id="inner" class="container">
    <ul>
      <li class="nav"><a href="#">One</a></li>
      <li class="nav"><a href="#">Two</a></li>
    </ul>
  </div>
</div>

А вот CSS для этого примера:

/* специфичность: 0101 */
#outer a {
  background-color: red;
}

/* специфичность: 0201 */
#outer #inner a {
  background-color: blue;
}

/* специфичность: 0104 */
#outer div ul li a {
  color: yellow;
}

/* специфичность: 0113 */
#outer div ul .nav a {
  color: white;
}

/* специфичность: 0024 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* специфичность: 0023 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* специфичность: 0033 */
div div .nav:nth-child(2) a:hover {
  border: 10px double black;
}

a {
  display: inline-block;
  line-height: 40px;
  font-size: 20px;
  text-decoration: none;
  text-align: center;
  width: 200px;
  margin-bottom: 10px;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
}

В результате получаем:

Что здесь происходит? Прежде всего, нас интересуют только семь первых правил этого примера, и, как вы заметили, мы указали специфичность в комментариях перед каждым из них.

  • Первые два соревнуются за цвет фона ссылок — второе побеждает и делает фон синим, поскольку у него на один селектор ID в цепочке больше:  специфичность 201 против 101.
  • Третий и четвертый соревнуются за цвет текста ссылок — последнее побеждает и делает их его белым - у него на один селектор элемента меньше, но вместо него добавлен селектор класса, стоящий в десять раз больше. Так что он побеждает со счетом 113 против 104.
  • Правила 5–7 соревнуются за то, чтобы определить, как быдет выглядеть граница ссылки при наведении на нее указателя мыши. Шестое проигрывает пятому со специфичностью 23 против 24 — у него на один селектор элемента в цепочке меньше. Седьмое, однако, бьет обоих — селекторов столько же, сколько в пятом, но один из селекторов элемента заменен на селектор класса. Так что седьмое побеждает со счетом 33 против 23 и 24.

Примечание: Просмотрите еще раз все селекторы, если вы еще этого не сделали, просто чтобы убедиться, что вы поняли, как во всех этих случаях вычислялась специфичность.

Порядок следования

Как было указано ранее, если несколько конкурирующих селекторов обладают одинаковой важностью (importance) и специфичностью (specifity), то победа определяется порядком следования  — те, что идут позднее, побеждают более ранние. Рассмотрим пример:

p {
  color: blue;
}

/* Это правило победит предыдущее */
p {
  color: red;
}

А вот в этом примере побеждает первое правило, поскольку здесь исходный порядок перекрывается специфичностью:

/* Это правило победит */
.footnote {
  color: blue;
}

p {
  color: red;
}

Замечание о наложении правил

Рассматривая всю эту теорию, касающуюся каскадирования, и того, как одни стили применяются поверх других, важно помнить, что все это происходит на уровне свойств — одни свойства переопределяют другие свойства, но целые правила не переопределяют другие правила. Если некоторому элементу отвечают несколько CSS правил, они все применяются к нему. И только после этого выявляются отдельные конфликтующие свойства и определяется, какие именно стили победят.

Рассмотрим пример. Сначала HTML:

<p>I'm <strong>important</strong></p>

и немного CSS, чтобы оформить его:

/* специфичность: 0002 */
p strong {
  background-color: khaki;
  color: green;
}

/* специфичность: 0001 */
strong {
  text-decoration: underline;
  color: red;
}

Результат:

В данном примере, в силу большей специфичности, свойство цвета (color) из первого правила переопределит аналогичное свойство второго правила. Однако свойства background-color из первого правила и text-decoration из второго оба применяются к элементу <strong>. Обратите внимание, также, что текст данного элемента имеет жирный шрифт: это свойство прешло из таблицы стилей, применяемой в браузере по умолчанию.  

Наследование

CSS наследование — последняя часть информации, позволяющей нам понять, какой стиль применится к элементу. Дело в том, что одни свойсва элемента применяются к его потомкам, а другие - нет.

  • Например, свойства типа (font-family) и цвета (color) шрифта имеет смысл сделать наследуемыми, чтобы можно было задать базовый шрифт для всего сайта, применив их к элементу <html>; а где нужно эти установки можно перекрыть. Задание базового шрифта для каждого элемента заняло бы уж очень много времени.
  • А вот свойства полей (margin), отступа (padding), границы border и фонового рисунка (background-image) наследовать как раз НЕ следует. Можно себе представить, какая путаница возникла бы, если бы после установки этих свойств для контейнера их пришлось бы переписывать для каждого из потомков!

То, какие из свойств наследуются по умолчанию, а какие - нет, можно, в основном, понять из соображений здравого смысла. основано, во многом, на здравом смысле. Тем не менее, для уверенности можно обратиться к справочнику CSS (CSS Reference) — страница каждого свойства начинается с таблицы, в которой подытожены его основные характеристики, в том числе и то, является ли оно наследуемым.

Управление наследованием

В CSS есть три значения, позволяющие управлять наследованием:

  • inherit : Это значение устанавливает для свойства элемента такое же значение, как и у его родителя.
  • initial : Это значение устанавливает свойство выбранного элемента равным тому, что задается для него в таблице стилей браузера по умолчанию. Если в таблице стилей браузера это значение не установлено, а исходно оно являлось наследуемым, то оно устанавливается равным inherit.
  • unset : Это значение сбрасывает свойство к его исходному значению, то есть если исходно оно является наследуемым, то оно работает как inherit, а в противном случае - как initial.

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

Рассмотрим это на примере. Сначала HTML:

<ul>
  <li>Default <a href="#">link</a> color</li>
  <li class="inherit">Inherit the <a href="#">link</a> color</li>
  <li class="initial">Reset the <a href="#">link</a> color</li>
  <li class="unset">Unset the <a href="#">link</a> color</li>
</ul>

Теперь CSS:

body {
  color: green;
}

.inherit a {
  color: inherit;
}

.initial a {
  color: initial
}

.unset a {
  color: unset;
}

Получаем:

Теперь объяснение, как что здесь происходит:

  • Сначала мы устанавливаем свойство color элемента <body> равным green (зеленый).
  • Поскольку свойство color исходно является наследуемым, все потомки элемента body будут иметь тот же, что и у него, зеленый цвет. Следует заметить, что по умолчанию браузер задает для ссылок голубой цвет шрифта, вместо наследуемого, так что первая ссылка в нашем списке голубая.
  • Второе правило указывает, что ссылки внутри элементов класса inherit наследуют свойство color от своего родителя. В данном случае это означает, что ссылка наследует цвет (color) от своего родителя <li>, который, по умолчанию, наследует его от собственного родителя <ul>, который, в конце концов, наследует цвет от элемента <body>, у которого свойство цвета cssxref("color")}} установлено равным green в первом правиле.
  • Третье правило отбирает все ссылки внутри элементов класса initial  и устанавливает их значение цвета равным  initial. Обычно по умолчанию браузер задает для текста черный цвет, так что эта ссылка черная.
  • Последнее правило отбирает все ссылки внутри элементов класса unset и устанавливает их значение цвета (color) равным unset — то есть сбрасывает его в значение по умолчанию. Поскольку исходно свойство color является наследуемым, оно работает, как если бы мы установили для него значение inherit. В результате, эта ссылка получает тот же цвет, что и элемент body — зеленый.

Упражнение: потренируемся в каскадировании

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

При ошибке изменения можно сбросить кнопкой Reset . Если застрянете, можно посмотреть вариант ответа с помощью кнопки Show solution.

Что дальше

Если вы поняли большую часть этой статьи, отлично — вы начали осознавать основы CSS. Осталась блоковая модель, которую мы рассмотрим в слудующей статье.

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

Метки документа и участники

 Внесли вклад в эту страницу: mariag, EmilDavletov, Sokrat83
 Обновлялась последний раз: mariag,