Сколько весят селекторы? • Про CSS

Все CSS-селекторы имеют свой вес, который определяет как взаимодействуют одинаковые свойства, заданные в разных местах кода одному и тому же элементу.

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

Вот пример проблемы. Есть див с id="container", внутри него некоторый текст и список ссылок.

<div>
  <p><a href="#">link in P</a></p>
  <ul>
    <li><a href="#">Link1</a></li>
    <li><a href="#">Link2</a></li>
  </ul>
</div>

Сначала задаём всем ссылкам внутри #container оранжевый фон:

#container A {
  background: orange;
}

А потом, чтобы в списке .list внутри контейнера ссылки имели зелёный фон, ниже дописываем такое:

.list A {
  background: mediumspringgreen;
}

Казалось бы, ссылки в тексте должны получить оранжевый фон, а ссылки в списке — зеленый, но нет:

Почему так? Потому, что первый селектор содержит ID и перевешивает второй, то есть:

#container A > . list A

Вес селектора

Специфичность селектора рассчитывается по 4-м позициям:

Для каждого из них подсчитывается количество подходящих элементов в селекторе, и это число помещается в соответствующую позицию.

Пример:

Вес селекторов (по убыванию):

style=""1,0,0,0

#id0,1,0,0

.class0,0,1,0

[attr=value]0,0,1,0

LI0,0,0,1

*0,0,0,0

У стилей, заданных в атрибуте style, на первой позиции будет единица — 1,0,0,0. Это самая высокая специфичность, которая перевешивает свойства, заданные другими способами.

Переопределить стили, заданные в style, можно дописав !important к значению свойства в таблице стилей.

Обратный вариант — универсальный селектор *, он не имеет веса: 0,0,0,0.

Примеры:

LI0,0,0,1 — селектор по тегу

UL LI0,0,0,2 — селектор c двумя тегами весит больше, чем с одним.

.orange0,0,1,0 — селектор с классом весит больше, чем селектор с тегом.

.orange A SPAN0,0,1,2 — селектор перевесит предыдущий, потому что помимо класса содержит два тега.

#page .orange0,1,1,0 — селектор с ID перевесит всё, кроме inline-стилей.

Теперь сравним селекторы из исходного примера:

#container A0,1,0,1

.list A0,0,1,1

0,1,0,1 > 0,0,1,1 — хорошо видно, что селектор с ID весит больше, чем селектор с классом, поэтому все ссылки имеют оранжевый фон, хотя ниже в коде им задан зеленый.

Варианты решений

1. Добавить !important

#container A {
  background: orange;
}
.list A {
  background: mediumspringgreen !important;
}

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

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

2. Следующий очевидный способ — добавить #container ко второму селектору, чтобы увеличить его вес:

#container A {
  background: orange;
}
#container .list A {
  background: mediumspringgreen;
}

Это тоже сработает, но решение так себе: удлиняется цепочка селекторов (что может отразиться на скорости отрисовки страницы) и ухудшается читаемость кода. Так тоже делать не стоит.

1-й и 2-й способ могут использоваться, если у вас нет доступа к разметке, а в ней нет нужных классов. Если же вы можете редактировать разметку либо классы у элементов таки есть — используйте последний способ, самый правильный:

3. Просто не используйте в стилях селекторы с ID, используйте классы.

Посмотрим на разницу между #container и с .container:

#container A

0,1,0,1 — селектор с ID перевешивает всё вне зависимости от своего расположения в коде.

Заменим в разметке страницы id на class:

.container A0,0,1,1 — селектор с классом весит меньше, он менее специфичен.

Селектор ссылок в списке весит столько же:

.list A0,0,1,1

Это означает, что при равном весе селекторов применятся стили, объявленные ниже в коде. То есть достаточно будет просто написать стили, следуя от общего к частному, сверху вниз.

В итоге разметка может быть такой:

<div>
  <p><a href="#">link in P</a></p>
  <ul>
    <li><a href="#">Link1</a></li>
    <li><a href="#">Link2</a></li>
  </ul>
</div>

А стили — такими:

.container A {
  background: orange;
}
.list A {
  background: mediumspringgreen;
}

И код работает так, как ожидается:

Если id в вашей разметке уже используется в Js, логичнее будет добавить элементу класс и перевесить стили на него. Если же id участвует только в разметке — лучше заменить его на class.

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

Спецификации:

  • w3.org/TR/CSS2/cascade.html#specificity
  • w3.org/TR/css3-selectors/#specificity

Понимание веса CSS-селекторов

Введение

Как и любая моя статья, эта начинается с введения. Дабы не говорить на разных языках, начнём с самых основ в мире CSS, то есть с терминологии. Чувствую себя преподавателем в университете — как же это круто.

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

Селектор — это строка, представляющая собой формальное описание структуры, на основе которого выбирается элемент или группа элементов в дереве документа и применяется объявленный блок свойств.

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

Я не буду вдаваться в подробности того, что селекторы должны начинаться с буквы и некоторых других символов. Я также не буду говорить про неинтересные прописные истины, которые можно найти в любой статье на тему основ CSS. Речь в этой статье пойдёт про вес CSS-селекторов, а если говорить простым языком — о его числовом представлении и понимании.

Вес селекторов

Нет, здесь не идёт речь об их реальном весе (его нет), а также о количестве символов. Такой вес нельзя измерить весами, так как он не вещественен в обычном понимании и вообще не существует в нашем с вами мире людей. Зато он есть в виде нулей и единиц. Получается, что селектор можно взвесить, используя некую систему, о которой мы и будем говорить дальше.

Что такое вес селектора?

Вес селектора — это условные четыре позиции x, x, x, x, которые заполняются нулями и единицами в соответствии с содержимым селектора. Каждая из позиций имеет своё содержимое:

  • Инлайн стили
  • Идентификаторы
  • Классы, атрибуты и псевдоклассы
  • Теги и псевдоэлементы

Как это читать?

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

Как заполнять?

Немного сложнее, чем читать. Рассмотрим предложенную систему четырёх позиций на реальном примере для того, чтобы понять всю технологию.

h2 {
  color: #777;
}

В этом примере селектором выступает заголовок

h2, который состоит из одного тега. Получается, что напротив столбца «тег» мы ставим единичку. Получается следующая картина: 0, 0, 0, 1.

Всё это замечательно, но в реальных проектах встречается разве что в ядре стилей или normalize, а это значит, что нужно усложнить задачу.

#main .container article.post > header h2.giga {
  color: #777;
}

Пример получился хардкорнее, чем предыдущий и в реальной жизни достоин премии «самого избыточного селектора». Такая избыточность должна преследоваться по закону, но подробнее на эту тему мы поговорим в отдельной части статьи.

Окей, вернёмся к примеру и немного поработаем весами.

Давайте начнём слева, так как в начале стоит единственный идентификатор #main. Далее мы видим три класса .container, .post и .giga, а также три тега article, header и h2. Для ещё большей наглядности я распишу это в виде этапов:

// Селектор
#main .container article.post > header h2.giga
// Начальный вес
0, 0, 0, 0
// Идентификаторы
#main         0, 1, 0, 0
// Классы, атрибуты и псевдоклассы
.container    0, 1, 1, 0
.post         0, 1, 2, 0
.giga         0, 1, 3, 0
// Теги и псевдоэлементы
article       0, 1, 3, 1
header        0, 1, 3, 2
h2            0, 1, 3, 3
// Итог
#main .container article.post > header h2.giga
=>
0, 1, 3, 3

Давайте напишем какой-нибудь безбашенный селектор, который я, надеюсь, никогда не увижу ни у кого в коде:

// Селектор
body.page-posts #main .container article.post ul.list-unstyled > li:first-child h3. article-title:hover {
  color: #333;
}
// Начальный вес
0, 0, 0, 0
// Идентификаторы
#main             0, 1, 0, 0
// Классы, атрибуты и псевдоклассы
.page-posts       0, 1, 1, 0
.container        0, 1, 2, 0
.post             0, 1, 3, 0
.list-unstyled    0, 1, 4, 0
:first-child      0, 1, 5, 0
.article-title    0, 1, 6, 0
:hover            0, 1, 7, 0
//  Теги и псевдоэлементы
body              0, 1, 7, 1
article           0, 1, 7, 2
ul                0, 1, 7, 3
li                0, 1, 7, 4
h3                0, 1, 7, 5
// Итог
body.page-posts #main .container article.post ul.list-unstyled > li:first-child h3.article-title:hover
=>
0, 1, 7, 5

Ну и напоследок, для полного понимания темы, будет пример с атрибутами и псевдоэлементами.

// Селектор
.main[data-columns]:before {
  content: "3 .column.size-1of3";
}
// Начальный вес
0, 0, 0, 0
// Идентификаторы
0, 0, 0, 0
// Классы, атрибуты и псевдоклассы
.main             0, 0, 1, 0
[data-columns]    0, 0, 2, 0
//  Теги и псевдоэлементы
:before           0, 0, 2, 1
// Итог
.
main[data-columns]:before => 0, 0, 2, 1

Вот такие вот дела.

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

А что, если вес селекторов одинаковый?

Допустим, что у вас есть два или несколько селекторов так или иначе указывающих на один и тот же элемент. И вот так сложилось, что вы посчитали или просто взглянули на них, и вес оказался одинаковым. Не стоит отчаиваться, просто блок объявлений последнего селектора в вашем CSS-коде из этой группы и будет применяться к элементу. Как-то так. Мне кажется это логичным. Прямо как в поговорке «кто не успел, тот опоздал», но наоборот: «кто опоздал, тот и успел».

Зачем это нужно?

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

Ещё одним применением такого вот расчёта являются сервисы или скрипты, которые строят диаграммы специфичности CSS. Это может быть очень удобно для анализа и оценки избыточности вашего кода.

Интересным фактом будет то, что единственный раз, когда мне приходилось считать вес селектора, был тестом от Mail.ru на какой-то сертификат. Если интересно, то я поищу этот тест у себя в истории.

Я ленивый и не хочу понимать, как считать вес селекторов, что тогда?

Ох, специально для вас у меня есть сервис, на который я наткнулся при подготовке к изложению этого материала: Specificity Calculator — это простой и эффективный калькулятор веса селекторов.

Специфичность селекторов

Раз уж зашла речь про вес CSS-селекторов, то неизбежно задумываешься о том, как бы его оценить: когда нужно ему худеть, а когда наоборот — поправляться. Как и у человека, у селекторов всё таки есть оптимальный вес.

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

Хорошо, всё это замечательно, но причём тут вес? — да очень просто, он напрямую от этого зависит. Чем больше вложений, тем больше вес. Логично, однако.

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

Выводы

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

Специфика CSS

❮ Предыдущая Далее ❯


Что такое специфичность?

Если есть два или более правила CSS, которые указывают на один и тот же элемент, селектор с наибольшим значением специфичности «выиграет», а его объявление стиля будет применено к этому элементу HTML.

Думайте о специфичности как о балле/ранге, который определяет, какое объявление стиля в конечном итоге применяется к элементу.

Посмотрите на следующие примеры:

Пример 1

В этом примере мы использовали элемент «p» в качестве селектора и указали красный цвет для этого элемента. Текст будет красным:



 

Hello World!


Попробуйте сами »

Теперь посмотрите на пример 2 :

Пример 2

В этом примере мы добавили селектор класса (названный «тест») и указан зеленый цвет для этого класса.

Текст теперь будет зеленым (несмотря на то, что мы указали красный цвет для селектора элемента «p»). Это потому, что селектор класса дан более высокий приоритет:



 

Hello World!


Попробуйте сами »

Теперь посмотрите на пример 3:

Пример 3

В этом примере мы добавили селектор id (названный «демо»). Теперь текст будет синий, потому что селектор id имеет более высокий приоритет:



 

Здравствуйте! Мир!


Попробуйте сами »

Теперь посмотрите на пример 4:

Пример 4

В этом примере мы добавили встроенный стиль для элемент «п». текст теперь будет розовым, потому что встроенный стиль имеет наивысший приоритет:



 

Привет, мир!


Попробуйте сами »


Иерархия специфичности

Каждый селектор CSS занимает свое место в иерархии специфичности.

Существует четыре категории, определяющие уровень специфичности селектора:

  1. Встроенные стили — Пример:

  2. ID — Пример: #навбар
  3. Классы, псевдоклассы, селекторы атрибутов — Пример: .test, :hover, [ссылка]
  4. Элементы и псевдоэлементы — Пример: h2, ::before

Как рассчитать специфичность?

Запомните, как рассчитывать специфичность!

Начните с 0, добавьте 100 для каждого значения идентификатора, добавьте 10 для каждого значение класса (или селектор псевдокласса или атрибута), добавьте 1 для каждого селектора элемента или псевдоэлемента.

Примечание. Встроенный стиль получает значение специфичности 1000 и всегда дается наивысший приоритет!

Примечание 2: Есть один исключение из этого правила: если вы используете !important правило, он даже переопределяет встроенные стили!

В таблице ниже приведены некоторые примеры расчета значений специфичности:

Селектор Значение специфичности Расчет
стр. 1 1
п.тест 11 1 + 10
р#демо 101 1 + 100

1000 1000
#демо 100 100
.тест 10 10
п.тест1.тест2 21 1 + 10 + 10
#navbar p#demo 201 100 + 1 + 100
* 0 0 (универсальный селектор игнорируется)

Селектор с наивысшим значением специфичности выиграет и вступит в силу!

Рассмотрим эти три фрагмента кода:

Пример

A: h2
B: h2#content
C:

Заголовок

Специфичность A равна 1 (селектор одного элемента)
Специфичность B равна 101 (одна ссылка на идентификатор + один селектор элемента)
Специфичность C равна 1000 (встроенный стиль)

Поскольку третье правило (C) имеет наивысшее значение специфичности (1000), этот стиль декларация будет применяться.



Дополнительные примеры правил специфичности

Равная специфичность: последнее правило побеждает — Если одно и то же правило дважды записано во внешнюю таблицу стилей, то последнее правило выигрывает:

Пример

h2 {background-color: yellow;}
h2 {background-color: red;}

Попробуйте сами »


Селекторы ID имеют более высокую специфичность, чем селекторы атрибутов — Посмотрите на следующие три строки кода:

Пример

div#a {цвет фона: зеленый;}
#a {цвет фона: желтый;}
div[id=a] {цвет фона: синий; }

Попробуйте сами »

первое правило является более точным, чем два других, поэтому оно будет применяться.


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

Пример

Из внешнего файла CSS:
#content h2 {background-color: red;}

В файле HTML:

будет применяться последнее правило.


Селектор класса превосходит любое количество селекторов элементов — селектор класса, такой как .intro, превосходит h2, p, div и т. д.:

Пример

.intro {фоновый цвет: желтый;} цвет: red;}

Попробуйте сами »


Универсальный селектор (*) и унаследованные значения имеют специфичность 0 — Универсальный селектор (*) и унаследованные значения игнорируются!

❮ Предыдущий Далее ❯


В чем специфика селектора CSS?

Как следует из названия, Каскадная таблица стилей — это система стилей, используемая для оформления HTML-элементов. Эти стили каскадируются сверху вниз, где стиль над другим в том же элементе будет перезаписан стилем под ним.

Стили, применяемые к HTML, выполняются с помощью селектора. Селектор CSS позволит вам выбрать конкретный элемент HTML, который вы хотите стилизовать. Есть три основных способа добиться этого:

Типы селекторов CSS

1.
Селектор типа

Селектор типа CSS позволит вам выбрать на его базе HTML-элемент . С помощью такого селектора каждый элемент HTML-кода с указанным будет стилизован. Например:

 
<голова>
<стиль>
п{
красный цвет;
цвет фона: желтый;
}


<тело>
 

Это пример пункта

<тело>
2. Селектор класса

Селектор класса используется для выбора элементов с одинаковым именем класса для их стилизации в таблице стилей.

 
<голова>
<стиль>
.pTry{
красный цвет;
цвет фона: желтый;
}


<тело>

Это пример паракласса

<тело>

В стилях вы начинаете имя класса с точки ( . )

3. Селектор ID

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

 
<голова>
<стиль>
#pПопробовать{
красный цвет;
цвет фона: желтый;
}


<тело>
 

Это пример идентификатора пункта

<тело>

В стилях вы начинаете имя идентификатора со знака решетки ( # )

У нас также есть псевдоселекторы.

Специфика селектора CSS

Как упоминалось ранее, стили, примененные к элементам, расположенным поверх каскада, будут перезаписаны стилями, примененными под ними к тому же элементу.

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

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

Специфика селекторов

Различные типы селекторов имеют следующую специфику:

  • Селектор типа имеет наименьший вес специфичности и содержит балльную оценку 0-0-1.

  • Селектор класса имеет средний вес специфичности и содержит балльную оценку 0-1-0.

  • Селектор ID имеет самый высокий вес специфичности 1-0-0.