Взвешиваем селекторы CSS. Приоритеты стилей в CSS

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

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

Как это выглядит

Специфичность представляет собой группу из четырех чисел. Например, 0,0,0,0 или 0,0,1,2.

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

Специфичность считается по селектору. Правила подсчета очень просты:

  • каждый присутствующий в селекторе идентификатор добавляет к специфичности 0,1,0,0;
  • каждый класс, псевдокласс или атрибут добавляет к специфичности 0,0,1,0;
  • каждый элемент или псевдоэлемент добавляет к специфичности 0,0,0,1;
  • универсальный селектор и комбинаторы не учитываются.

Давай закрепим полученные знания и поупражняемся в расчете специфичности.

P {/*какие-то определения */} div p {/*какие-то определения */} p.note {/*какие-то определения */} form.feedbackForm input {/*какие-то определения */} #conten a:hover {/*какие-то определения */}

Превая строка — одинокий селектор типа. Специфичность 0,0,0,1.

Вторая строка — два селектора типа. Специфичность 0,0,0,2.

Теретья строка — селектор типа и класса. Специфичность 0,0,1,1.

Четвертая строка — два селектора типа, один класса и один атрибута. Специфичность 0,0,2,2.

Пятая строка — селектор идентификатора, типа и псевдокласс. Специфичность 0,1,1,1.

Кто победил?

Сравниваются специфичности очень просто. Какое число больше, то определение и выиграло.

Например:
0,0,1,4 больше, чем 0,0,1,2.
0,1,2,0 больше, чем 0,0,2,1.

Учти, что первые (те, которые левее) числа всегда возьмут верх над последующими (более правыми). Они как бы находятся в более старшем разряде.

Например, 0,1,0,0 больше, чем 0,0,8,9.

Если тебе так удобнее, можешь мысленно отбросить запятые и рассматривать предыдущий пример, как 100>89. Только не запутайся, если какой-то из разрядов специфичности будет больше девяти (что может быть при сильно навороченном селекторе). Например, если получилась специфичность 0,1,10,14, запятые отбрасывать нельзя, а то все разряды попутаются.

Специфичность и объявления

Специфичность относится как бы не ко всему правилу в целом, а к каждому конкретному объявлению. Поэтому, может получиться что правило «отработает» не полностью. Например:

Подопытный текст

Div { color: #0f0; /* Специфичность 0,0,0,1. */ font-weight: bold; /* Специфичность 0,0,0,1. */ } .box { font-weight: normal; /* Специфичность 0,0,1,0. */ }

Объявление из строки 2 для элемента

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

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

Обрати внимание, что специфичность селектора идентификатора (0,1,0,0) всегда выше, чем селектора атрибута (0,0,1,0). Даже если этот атрибут — ! Поэтому:

Form.feedback input { color: #f00; /* Специфичность 0,0,2,2. */ } #name { color: #0f0; /* Специфичность 0,1,0,0. - победа! */ }

Встроенные стили

По идее, кто-то уже давно должен был спросить, а зачем, собственно, в специфичности самая первая цифра? Ведь мы ее до сих пор никак не использовали!

Все верно. Первая цифра зарезервирована для встроенных стилей. Считается, что их специфичность равна 1,0,0,0. Таким образом, встроенный стиль всегда перебивает стиль заданный во внешней или вложенной таблице стилей. Например:

Этот текст будет зеленым

Даже использовав селектор идентификатора, мы не перебъем встроенный стиль.

Div#box { color: #00f; /* Специфичность 0,1,0,1. - маловато */ }

Это что же получается? На встроенные стили нет никакой управы?

Не волнуйся. Конечно, есть способ перебить встроенные стили (а заодно и все остальные объявления).

Объявление!important

Если очень нужно, можно пометить какое нибудь объявление, как важное (important). Такое объявление будет считаться заведомо победившим при сравнивании специфичностей. Да, да, в том числе победившем и встроенные стили. Давай немного изменим CSS для предыдущего примера:

Div { color: #00f !important; /* важное объявление - сразу победа! */ }

Даже слабенький по специфичности (0,0,0,1) селектор типа перебил встроенный стиль, ведь его объявление теперь стало важным!

Детали применения!important хорошо описаны в нашем CSS-справочнике . Если хочешь узнать подробности — перейди по ссылке .

Теперь, вооружившись знанием о расчете специфичности можно продолжить изучение наследования в CSS.

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

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

  1. Никогда не использовать ID селекторы в CSS . У них нет никаких преимуществ по сравнению с class .
    • Все, что можно сделать с ID , можно сделать и с class .
    • ID не могут быть переиспользованны.
    • Вес ID очень большой. Перебить ID нельзя даже сотней цепных class-ов .
  2. Не создавайте излишних селекторов. Если .header-nav {} прекрасно работает, то не используйте определение .header .header-nav {} . В этом случае ничего не изменится и никакой выгоды от этого не будет.
  3. Не конкретизируйте селекторы, пока это действительно не понадобится. Если .nav {} работает, то не используйте ul.nav {} . Такая запись лишь сократит варианты использования данного класса .nav , а так же повысит вес селектора без очевидной пользы.
  4. Заставьте себя использовать .class , потому что это идеальные селекторы.

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

Это все очень простые правила и следовать им не так-то трудно.

Уменьшение веса ID

Предположим у вас есть виджет на странице и вы хотите его стилизовать:

...

И, например, мы не можем изменять HTML код виджета, чтобы избавиться от ID. Поэтому мы делаем так:

#widget { ... }

В результате мы имеем определение для ID в CSS файле, что совсем нехорошо. Вместо этого мы можем сделать следующее:

{ ... }

Это селектор атрибута. В данном случае это уже определение не для ID , а для элемента. Если говорить точно, то селектор говорит: «Эй, найди мне элемент, у которого есть атрибут id со значением widget ».

Прелесть такого подхода в том, что мы снизили вес ID до веса класса. Но это хак.

Безопасное увеличение веса

Увеличить вес селектора мы можем так:

Btn.btn.btn.btn { ... }

Но я надеюсь, что никогда не придется использовать такую запись на проектах.

Здесь мы видим, что цвет, заданный в .box a {} , перетирает цвет текста кнопки. В итоге текст сливается с фоном кнопки.

Конечно, мы можем исправить это, если поставим !important (jsfiddle.net/csswizardry/3N53n/1), но нет, спасибо, избавимся от этого!

Мы можем добавить дополнительный селектор в секцию .btn {} (23 строка) jsfiddle.net/csswizardry/3N53n/2 , но это не самое лучшее решение. Что, если проблема с кнопкой будет не только в .box , а где-либо еще? Каждый раз добавлять новый селектор – плохой вариант.

Поэтому мы продублируем .btn.btn : http://jsfiddle.net/csswizardry/3N53n/3

Это так же не самое лучшее решение, но все же мы увеличили вес селектора и цвет на кнопке теперь такой, какой и должен быть.

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

От автора: представьте специфичность в виде оценки или степени, которая решает, какие стили применить к элементу. Универсальный селектор (*) имеет низкую специфичность. Селектор id – высокую. Родительские селекторы типа p img и дочерние селекторы типа.panel > h2 имеют более высокую специфичность, чем типовые селекторы p, img и h1.

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

проигнорировать универсальный селектор.

Значения А, В и С вместе дают конечное значение специфичности. ID селектор типа #foo имеет специфичность 1,0,0. Селекторы атрибутов типа и классы типа.chart имеют специфичность 0,1,0. Если добавить псевдокласс типа:first-child (например, .chart:first-child), специфичность станет 0,2,0. А простые типовые или элементные селекторы типа h1 и p дают всего лишь 0,0,1.

Заметка: вычисление специфичности

Выучить и вычислить специфичность селектора можно с помощью ресурсов Specificity Calculator от Keegan Street и CSS Explain от Joshua Peek.

Сложные селекторы и комбинаторы дают, естественно, большую специфичность. Разберем пример CSS:

ul#story-list > .book-review { color: #0c0; } #story-list > .book-review { color: #f60; }

ul #story-list > .book-review {

color : #0c0;

#story-list > .book-review {

color : #f60;

Эти правила похожи, но не одинаковы. В первом селекторе ul#story-list > .bookreview находится типовой селектор (ul), ID селектор (#story-list) и класс (.bookreview). Специфичность равна 1,1,1. Во втором селекторе #story-list > .book-review хранятся только ID и класс. Специфичность равна 1,1,0. Несмотря на то, что #story-list > .book-review объявлен ниже ul#story-list > .bookreview, высокая специфичность последнего заставить элементы с классом.book-review окраситься в зеленый, а не оранжевый цвет.

Псевдоклассы:link и:invalid имеют ту же специфичность, что и классы. У a:link и a.external будет одна специфичность, равная 0,1,1. Точно так же псевдоэлементы типа::before и::after имеют одинаковую специфичность с типовыми и элементными селекторами. Если два селектора имеют одинаковую специфичность, в дело вступает каскадирование. Пример:

a:link { color: #369; } a.external { color: #f60; }

a : link {

color : #369;

a . external {

color : #f60;

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

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

Заключение

После прочтения этой главы у вас должно сложиться четкое понимание CSS селекторов. В частности вы должны знать:

как использовать селекторы и применять стили к конкретным элементам, псевдоэлементам и псевдоклассам;

понимать различия между псевдоэлементами и псевдоклассами;

использовать новые псевдоклассы, представленные в спецификациях Selectors Level 3 и 4;

вычислять специфичность.

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

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

Мы можем изменить этот абзац просто используя имя тега :

P { color: blue; }

Или же можем воспользоваться именем класса :

Message { color: green; }

Или можем использовать идентификатор :

#introduction { color: red; }

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

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

Порядок правил CSS

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

P { color: green; } p { color: red; } /* Абзац будет красным */

Вычисление 100

Есть один быстрый способ выяснить, насколько правило CSS «сильное», путём вычисления специфичности селекторов :

  • идентификаторы стоят 100;
  • классы стоят 10;
  • селекторы тега стоят 1.

Селектор с наивысшим «счётом» будет преобладать, независимо от порядка, в котором появляются правила CSS .

#introduction { color: red; } .message { color: green; } p { color: blue; }

MarkSheet - это бесплатное руководство по HTML и CSS.

Правило #introduction { color: red; } является более специфичным , чем другие, потому что идентификаторы должны быть уникальными по всей веб-странице, таким образом, может быть только один целевой элемент.

Message { color: green; } будет нацелен на любой HTML-элемент с атрибутом class="message" и, следовательно, менее специфичен. То же самое относится и к p { color: blue; } , который может предназначаться для любого абзаца.

Как избежать конфликтов

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

Чтобы избежать этого:

  • применяйте только классы : используйте .introduction вместо #introduction , даже если этот элемент появляется на вашей веб-странице только один раз;
  • избегайте применять несколько классов к одному элементу HTML: пишите не

    А

    Который является семантически более описательным;

  • не используйте встроенные стили , такие как
    .

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

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

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

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

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

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

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

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

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

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

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

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

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

H1 { color: #777; }

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

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

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

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

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

// Селектор #main .container article.post > header h1.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 h1 0, 1, 3, 3 // Итог #main .container article.post > header h1.giga => 0, 1, 3, 3

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

// Селектор body.page-posts #main .container article.post ul.list-unstyled > li:first-child h2.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 h2 0, 1, 7, 5 // Итог body.page-posts #main .container article.post ul.list-unstyled > li:first-child h2.article-title:hover => 0, 1, 7, 5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Выводы

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