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

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

Цель данного урока — углубить Ваше понимание основных концепций CSS — каскадов, спецификаций и наследования, — которые контролируют, как CSS добавляется в HTML и как разрешаются конфликты.

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

Знания: Базовая компьютерная грамотность, Установка базового ПО, базовые знания работы с файлами, основы HTML (Введение в HTML), и идея того как работает CSS (Введение в CSS.)
Цель: Изучить понятие о каскаде и специфичности, и как работает наследование CSS.

Конфликтующие правила

CSS (Cascading Style Sheets) означает Каскадные Таблицы Стилей и первое слово "каскадные" является невероятно важным для понимая, то как ведет себя каскад — ключевой момент в понимании CSS.

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

Также значимым является концепция наследования, которая заключается в том, что некоторые свойства CSS по умолчанию наследуют значения, установленные для родительского элемента текущего элемента, а некоторые не наследуют. Что также может стать причиной поведения, которого вы могли не ожидать.

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

Каскад

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

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

 

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

Различные типы селекторов ( селекторы элементов h1{...}, селекторы классов, селекторы идентификаторов и т.д ) имеют разной степени влияние на элементы страницы, это обозначают термином "специфичность". Чем более общее влияние оказывает селектор на элементы страницы тем меньше его специфичность, конкретность.

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

  • Селектор элементов ( например h1 ) менее специфичен - он выбирает все элементы этого типа на странице, поэтому более специфичные селекторы его перекроют.

  • Селектор класса более конкретен - он выберет только те элементы на странице, которые имеют указанный атрибут class, селектор класса применится после селектора элементов и поэтому перекроет его стили.

Например. У нас есть два правила, как указано в примере ниже, которые могут применяться к h1. Как можно убедиться, несмотря на то, что селектор элемента указан после селектора класса ( и в соответствии с правилами каскада должен был бы примениться последним ), однако к элементу будет применен стиль указанный в селекторе класса, так как он более конкретен ( специфичен ).

 

В деталях о выборе на основе специфичности и подобных вещах будет рассказано позже.

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

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

Например, если для элемента установлен color и font-family, то и все дочерние его элементы будут иметь этот же стиль цвета и типа шрифта, но только если они не имеют собственных определений этих стилей.

 

Также не все свойства родительских элементов наследуются его дочерними элементами. Например если вы установите свойство width равным 50% для элемента, оно не применится к его дочерним элементам. Если бы все свойства родительских элементов наследовались безусловно, правила CSS было бы черезвычайно трудно использовать!

Примечание: Справочные страницы CSS свойств содержат окно технической информации, обычно в нижней части страницы, в котором перечислены некоторые технические данные применения этого свойства, в том числе наследуется ли оно дочерними элементами. Например его можно увидеть на color property Specifications section.

Понимание взаимодействия этих концепций.

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

Особенности наследования.

Начнем с примера ниже. Мы имеем <ul>, с двумя уровнями неупорядоченных списков вложенных в него. Мы устанавливаем для внешнего, самого верхнего <ul> элемента стили  обводки, отступов и цвета шрифта.

Цвет шрифта в этом примере применяется и к непосредственным потомкам, и так же, и к опосредованным, т.е. вложенным в прямых потомков элементам. Далее мы добавили класс special к одному из вложенных <ul>, а в правилах для него определили другой цвет шрифта, с этого момента он и все его вложенные дети уже наследуют именно этот переопределенный стиль.

 

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

Какие свойства наследуются по умолчанию, а какие нет, чаще всего понятно интуитивно.

Контроль наследования

CSS предоставляет универсальные значения контроля наследования для всех свойств настроек стилей.

inherit

Устанавливает значение выбранного свойства для элемента таким же как у его предка. По сути включает наследование.

initial
Устанавливает значение указанного свойства элемента, к которому применияется правило, в соответствии с настройками браузера по умолчанию. Если в таблице стилей браузера отсутсвует значение этого свойства оно наследуется естественным образом.
unset
Возвращает свойству его естественное значение, если свойство наследуется естественным образом, оно становится унаследованным ( inherit ), иначе оно действует как initial.

Примечание: Существует также более новое свойство , revert, но оно имеет ограниченную поддержку браузерами.

Примечание: Смотрите Origin of CSS declarations в Introducing the CSS Cascade для более подробной информации о каждом из них, и о том как они работают.

Мы можем изучить влияние универсальных значений влияющих на наследование. В примере ниже мы можем изменять значения CSS и видеть как эти изменения вляют на отображение элементов в списках. Лучший способ изучить HTML и CSS - применять его в коде!

Например:

  1. Второй элемент списка имеет класс my-class-1. Правило описанное для этого класса применяет указанный цвет для наследования ко  вложенному элементу a.Как изменится цвет если это правило будет удалено?
  2. Понятно ли почему третий и четвертый элементы a имеют такой цвет? Если нет внимательно перечитайте описание значений для этих правил представленное выше.
  3. Какая из ссылок изменит цвет если вы зададите новое значение цвета для <a>? Например: a { color: red; }?

Сброс всех значений свойств

Короткое свойство CSS all может быть применено для одновременного перевода всех ( почти ) наследуемых свойств в одно из четырех указанных выше значений (inherit, initial, unset, или revert) . Это удобный способ изменения влияния наследования перед внесением новых изменений.

На примере ниже приведены два блока <blockquote>. К первому применились стили описанные для blockquote. Класс fix-this в который включен второй блок сбрасывает значения стилей с помощью инструкции all: unset;.

 

Вы можете поэкспериментировать с различными стилями и посмотреть как влияет инструкция all в этих случаях.

Понимание каскада

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

Здесь учитываются три фактора в порядке убывания их важности. Стоящие вначале  важнее стоящих вконце.

  1. Важность
  2. Специфичность
  3. Порядок следования

Мы рассмотрим их снизу в верх, чтобы увидеть как именно браузеры определяют какой CSS следует применить.

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

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

Specificity

Once you understand the fact that source order matters, at some point you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting, rule is applied. This is because that earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.

As we saw earlier in this lesson, a class selector has more weight than an element selector, so the properties defined on the class will override those applied directly to the element.

Something to note here is that although we are thinking about selectors, and the rules that are applied to the thing they select, it isn't the entire rule which is overwritten, only the properties which are the same.

This behavior helps avoid repetition in your CSS. A common practice is to define generic styles for the basic elements, and then create classes for those which are different. For example, in the stylesheet below I have defined generic styles for level 2 headings, and then created some classes which change only some of the properties and values. The values defined initially are applied to all headings, then the more specific values are applied to the headings with the classes.

 

Let's now have a look at how the browser will calculate specificity. We already know that an element selector has low specificity and can be overwritten by a class. Essentially a value in points is awarded to different types of selectors, and adding these up gives you the weight of that particular selector, which can then be assessed against other potential matches.

The amount of specificity a selector has is measured using four different values (or components), which can be thought of as thousands, hundreds, tens and ones — four single digits in four columns:

  1. Thousands: Score one in this column if the declaration is inside a style attribute, aka inline styles. Such declarations don't have selectors, so their specificity is always simply 1000.
  2. Hundreds: Score one in this column for each ID selector contained inside the overall selector.
  3. Tens: Score one in this column for each class selector, attribute selector, or pseudo-class contained inside the overall selector.
  4. Ones: Score one in this column for each element selector or pseudo-element contained inside the overall selector.

Note: The universal selector (*), combinators (+, >, ~, ' '), and negation pseudo-class (:not) have no effect on specificity.

The following table shows a few isolated examples to get you in the mood. Try going through these, and making sure you understand why they have the specificity that we have given them. We've not covered selectors in detail yet, but you can find details of each selector on the MDN  selectors reference.

Selector Thousands Hundreds Tens Ones Total specificity
h1 0 0 0 1 0001
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="en-US"] > .inline-warning 0 0 2 2 0022
#identifier 0 1 0 0 0100
No selector, with a rule inside an element's style attribute 1 0 0 0 1000

Before we move on, let's look at an example in action.

 

So what's going on here? First of all, we are only interested in the first seven rules of this example, and as you'll notice, we have included their specificity values in a comment before each one.

  • The first two selectors are competing over the styling of the link's background color — the second one wins and makes the background color blue because it has an extra ID selector in the chain: its specificity is 201 vs. 101.
  • The third and fourth selectors are competing over the styling of the link's text color — the second one wins and makes the text white because although it has one less element selector, the missing selector is swapped out for a class selector, which is worth ten rather than one. So the winning specificity is 113 vs. 104.
  • Selectors 5–7 are competing over the styling of the link's border when hovered. Selector six clearly loses to five with a specificity of 23 vs. 24 — it has one fewer element selectors in the chain. Selector seven, however, beats both five and six — it has the same number of sub-selectors in the chain as five, but an element has been swapped out for a class selector. So the winning specificity is 33 vs. 23 and 24.

!important

There is a special piece of CSS that you can use to overrule all of the above calculations, however you should be very careful with using it — !important. This is used to make a particular property and value the most specific thing, thus overriding the normal rules of the cascade.

Take a look at this example where we have two paragraphs, one of which has an ID.

 

Let's walk through this to see what's happening — try removing some of the properties to see what happens if you are finding it hard to understand:

  1. You'll see that the third rule's color and padding values have been applied, but the background-color hasn't. Why? Really all three should surely apply, because rules later in the source order generally override earlier rules.
  2. However, The rules above it win, because class selectors have higher specificity than element selectors.
  3. Both elements have a class of better, but the 2nd one has an id of winning too. Since IDs have an even higher specificity than classes (you can only have one element with each unique ID on a page, but many elements with the same class — ID selectors are very specific in what they target), the red background color and the 1 pixel black border should both be applied to the 2nd element, with the first element getting the gray background color, and no border, as specified by the class.
  4. The 2nd element does get the red background color, but no border. Why? Because of the !important declaration in the second rule — including this after border: none means that this declaration will win over the border value in the previous rule, even though the ID has higher specificity.

Note: The only way to override this !important declaration would be to include another !important declaration on a declaration with the same specificity later in the source order, or one with a higher specificity.

It is useful to know that !important exists so that you know what it is when you come across it in other people's code. However, we strongly recommend that you never use it unless you absolutely have to. !important changes the way the cascade normally works, so it can make debugging CSS problems really hard to work out, especially in a large stylesheet.

One situation in which you may have to use it is when you are working on a CMS where you can't edit the core CSS modules, and you really want to override a style that can't be overridden in any other way. But really, don't use it if you can avoid it.

The effect of CSS location

Finally, it is also useful to note that the importance of a CSS declaration depends on what stylesheet it is specified in — it is possible for users to set custom stylesheets to override the developer's styles, for example the user might be visually impaired, and want to set the font size on all web pages they visit to be double the normal size to allow for easier reading.

To summarize

Conflicting declarations will be applied in the following order, with later ones overriding earlier ones:

  1. Declarations in user agent style sheets (e.g. the browser's default styles, used when no other styling is set).
  2. Normal declarations in user style sheets (custom styles set by a user).
  3. Normal declarations in author style sheets (these are the styles set by us, the web developers).
  4. Important declarations in author style sheets
  5. Important declarations in user style sheets

It makes sense for web developers' stylesheets to override user stylesheets, so the design can be kept as intended, but sometimes users have good reasons to override web developer styles, as mentioned above — this can be achieved by using !important in their rules.

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, take a look at the solution here.

What's next

If you understood most of this article, then well done — you've started getting familiar with the fundamental mechanics of CSS. Next up, we'll look at selectors in detail.

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 here if you start to come across strange issues with styles not applying as expected. It could be a specificity issue.

In this module

  1. Cascade and inheritance
  2. CSS selectors
  3. The box model
  4. Backgrounds and borders
  5. Handling different text directions
  6. Overflowing content
  7. Values and units
  8. Sizing items in CSS
  9. Images, media, and form elements
  10. Styling tables
  11. Debugging CSS
  12. Organizing your CSS