MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Cascade and inheritance

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

В предыдущей статье мы разбирались с различными селекторами 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:

/* specificity: 0101 */
#outer a {
  background-color: red;
}

/* specificity: 0201 */
#outer #inner a {
  background-color: blue;
}

/* specificity: 0104 */
#outer div ul li a {
  color: yellow;
}

/* specificity: 0113 */
#outer div ul .nav a {
  color: white;
}

/* specificity: 0024 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* specificity: 0023 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* specificity: 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 versus 24 — у него на один селектор элемента в цепочке меньше. Седьмой селектор, однако, бьет обоих — у него столько же подселекторов в цепочке, как и у пятого, но селектор элемента заменен на селектор класса. Так что побеждает со счетом 33 против 23 и 24.

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

Source order

As mentioned above, if multiple competing selectors have the same importance and specificity, the third factor that comes into play to help decide which rule wins is source order — later rules will win over earlier rules. For example:

p {
  color: blue;
}

/* This rule will win over the first one */
p {
  color: red;
}

Whereas in this example the first rule wins because source order is overruled by specificity:

/* This rule will win */
.footnote {
  color: blue;
}

p {
  color: red;
}

A note on rule mixing

One thing you should bear in mind when considering all this cascade theory, and what styles get applied over other styles, is that all this happens at the property level — properties override other properties, but you don't get entire rules overriding other rules. When several CSS rules match the same element, they are all applied to that element. Only after that are any conflicting properties evaluated to see which individual styles will win over others.

Let's see an example. First, some HTML:

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

And now some CSS to style it with:

/* specificity: 0002 */
p strong {
  background-color: khaki;
  color: green;
}

/* specificity: 0001 */
strong {
  text-decoration: underline;
  color: red;
}

Result:

In this example, because of its specificity, the first rule's color property overrides the color property of the second rule. However, both the background-color from the first rule and the text-decoration from the second rule are applied to the <strong> element. You'll also notice that the text of that element is bolded: this comes from the browsers' default stylesheet.

Inheritance

CSS inheritance is the last piece we need to investigate to get all the information and understand what style is applied to an element. The idea is that some property values applied to an element will be inherited by that element's children, and some won't.

  • For example, it makes sense for font-family and color to be inherited, as that makes it easy for you to set a site-wide base font by applying a font-family to the <html> element; you can then override the fonts on individual elements where needed. It would be really annoying to have to set the base font separately on every element.
  • As another example, it makes sense for margin, padding, border, and background-image to NOT be inherited. Imagine the styling/layout mess that would occur if you set these properties on a container element and had them inherited by every single child element, and then had to unset them all on each individual element!

Which properties are inherited by default and which aren't is largely down to common sense. If you want to be sure however, you can consult the CSS Reference — each separate property page starts off with a summary table including various details about that element, including whether it is inherited or not.

Controlling inheritance

CSS provides three special values to handle inheritance:

  • inherit : This value sets the property value applied to a selected element to be the same as that of its parent element.
  • initial : This value sets the property value applied to a selected element to be the same as the value set for that element in the browser's default style sheet. If no value is set by the browser's default style sheet and the property is naturally inherited, then the property value is set to inherit instead.
  • unset : This value resets the property to its natural value, which means that if the property is naturally inherited it acts like inherit, otherwise it acts like initial.

The inherit value is the most interesting — it allows us to explicitly make an element inherit a property value from its parent.

Let's take a look at an example. First some 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>

Now some CSS for styling:

body {
  color: green;
}

.inherit a {
  color: inherit;
}

.initial a {
  color: initial
}

.unset a {
  color: unset;
}

Result:

Let's explain what's going on here:

  • We first set the color of the <body> to green.
  • As the color property is naturally inherited, all child elements of body will have the same green color. It's worth noting that browsers set the color of links to blue by default instead of allowing the natural inheritance of the color property, so the first link in our list is blue.
  • The second rule sets links within an element with the class inherit to inherit its color from its parent. In this case, it means that the link inherits its color from its <li> parent, which, by default inherits its color from its own <ul> parent, which ultimately inherits its color from the <body> element, which had its color set to green by the first rule.
  • The third rule selects any links within an element with the class initial and sets their color to initial. Usually, the initial value set by browsers for the text color is black, so this link is set to black.
  • The last rule selects all links within an element with the class unset and sets their color to unset — we unset the value. Because the color property is a naturally inherited property it acts exactly like setting the value to inherit. As a consequence, this link is set to the same color as the body — green.

Active learning: playing with the cascade

In this active learning we'd like you to experiment with writing a single new rule that will override the color and background color that we've applied to the links by default. Can you use one of the special values we looked at in the Controlling_inheritance section to write a declaration in a new rule that will reset the background color back to white, without using an actual color value?

If you make a mistake, you can always reset it using the Reset button. If you get really stuck, press the Show solution button to see a potential answer.

What's next

If you understood most of this article, then well done — you've started getting familiar with the fundamental mechanics of CSS. The last bit of central theory is the box model, which we'll cover next.

If you didn't fully understand the cascade, specificity, and inheritance, then don't worry! This is definitely the most complicated thing we've covered so far in the course, and is something that even professional web developers sometimes find tricky. We'd advise that you return to this article a few times as you continue through the course, and keep thinking about it. Refer back to here if you start to come across strange issues with styles not applying as expected. It could be a specificity issue.

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

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