Понимание веса CSS-селекторов. Самая страшная тайна. Зачем это нужно

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

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

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

Приоритеты стилей в зависимости от типа селектора

Да, селекторы CSS также влияют на стилевые приоритеты, причем в расчет берутся не только простые селекторы (классы, селекторы тегов, атрибуты и т.д.), но и составные (дочерние, соседние, селекторы потомков и т.д.).

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

А теперь давайте рассмотрим, каким же образом браузеры считают эти баллы.

  1. Универсальный селектор - количество начисляемых баллов равно нулю (0).
  2. Селекторы тегов и псевдоэлементы - по одному (1) баллу за каждый.
  3. Селекторы атрибутов , классы и псевдоклассы - по десять (10) баллов за каждый.
  4. Идентификаторы - по сто (100) баллов за каждый идентификатор находящийся в селекторе.
  5. Атрибут style - встроенные стили не используют селекторов, а указываются непосредственно внутри тегов элементов, но при этом они имеют самый высокий приоритет исчисляемый тысячей (1000) баллов.

Чтобы вы лучше поняли, как вычисляются эти виртуальные баллы, вот вам несколько простых примеров.

* { } /* 0 баллов */ em { } /* 1 балл */ p::first-letter { } /* 2 балла (один селектор тегов и один псевдоэлемент) */ p { } /* 11 баллов (по одному селектору тегов и атрибутов) */ div.fine .one { } /* 21 балл (два класса и один селектор тегов) */ #header a:hover { } /* 111 баллов (идентификатор, селектор тегов и псевдокласс) */

Как видите все довольно просто. Только вы не пугайтесь заранее, думая, что вам придется постоянно высчитывать эти баллы при создании своих таблиц стилей. На самом деле их никто никогда просто так не считает. Обычно про селекторный приоритет вспоминают только тогда, когда какие-то стили не хотят работать. Вот тут-то и начинаются «танцы с бубном» и поиски виновного. :)

Пример демонстрирующий приоритеты селекторов

Приоритеты селекторов

Обычный параграф.

Примечание.

Результат в браузере

Обычный параграф.

Примечание.

В этом примере цвет текста примечания черный, как и цвет остальных параграфов, хотя для него и указан зеленый, а используемое свойство находится ниже в коде. Но при этом стили рамки и внутренние отступы к примечанию были добавлены. Почему это произошло, я думаю, понятно. Выходом в данной ситуации будет перед классом note дополнительно поставить идентификатор #content, который увеличит вес селектора или применить правило !important , о котором читайте далее.

В данной главе подробно объясняется, почему каскадные таблицы стилей (Cascading Style Sheets, CSS) называются каскадными. Для начала давайте вспомним, какими способами можно добавить стиль на веб-страницу:

  • подключить внешнюю таблицу стилей;
  • добавить внутреннюю таблицу стилей в HTML-документ через тег . В итоге цвет тегов

    Будет красным.

    Это – один из способов управлять значимостью стилей. Еще один способ повысить приоритет – специально увеличить вес селектора, например, добавив к нему ID или класс.

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

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

    P {color: red !important;} p {color: green;}

    Также!important перекрывает inline-стили. Слишком частое применение!important не приветствуется многими разработчиками. В основном, данное объявление принято использовать лишь тогда, когда конфликт стилей нельзя победить иными способами.

    Сброс стилей с помощью reset.css

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

    Каждый браузер имеет свои отличия во встроенных стилях: например, в IE нет отступа от верхнего края окна, а в Firefox есть. Таких отличий существует много. Чтобы они не создавали помех для кроссбраузерности при написании собственного стиля CSS, можно воспользоваться методом сброса встроенных стилей.

    Инструмент для сброса стилей – это, по сути, та же самая таблица CSS, где описаны правила, которые сбрасывают встроенные стили браузеров, устанавливая базовые значения свойств. Называется такая таблица reset.css и служит для того, чтобы вы могли начать создавать стиль «с нуля». Вот пример стандартной таблицы сброса:

    Html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ""; content: none; } table { border-collapse: collapse; border-spacing: 0; }

    Допустим, у Вас есть несколько селекторов которые ссылаются на один и тот же элемент:

    Открыв HTML страницу где элемент span находится внутри элемента div мы увидим подчеркнутый текст. А если мы пойдем дальше и с помощью плагина Firebug посмотрим стили вложенного элемента, мы увидим следующее:

    Div span { text-decoration:underline; } span { text-decoration:none; }

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

    Правила специфичности

    Специфичность селекторов (selector"s specificity) определяет их приоритетность в таблице стилей. Чем специфичнее селектор, тем выше его приоритет.

    В спецификации по CSS 2.1 этой теме посвящен небольшой раздел. Существует 4 правила по которым вычисляется специфичность селекторов:

    1. Самый высокий приоритет имеет атрибут style . Это правило перекрывает все селекторы описанные в стилях.
    2. Второе место занимает присутствие ID в селекторе(#some-id).
    3. Далее идут все атрибуты(в том числе и атрибут class ) и псевдоклассы(pseudo-classes) в селекторе.
    4. Самый низкий приоритет у селекторов с именами элементов и псевдоэлементами(pseudo-elements).

    Все 4 правила сводятся в одну систему a-b-c-d (где а - наивысший приоритет) и образуют специфичность.

    Селектор Специфичность a-b-c-d Правило №
    * 0-0-0-0 -
    li 0-0-0-1 4
    li:first-line 0-0-0-2 4
    ul li 0-0-0-2 4
    ul ol+li 0-0-0-3 4
    form + * 0-0-1-1 3, 4
    table tr td.second 0-0-1-3 3, 4
    h2.block.title. 0-0-2-1 3, 4
    #xyz 0-1-0-0 2
    style=" " 1-0-0-0 1

    Пример специфичности - правило №1:

    content

    Текст внутри элемента p будет отображаться синим цветом независимо от селектора с айди(id), где также указано свойство color со значением red . Правило номер 1 всегда перекрывает все селекторы и имеет наивысшую приоритетность.

    Правило №2:

    • first
    • second

    Несмотря на то что селектор с id указан в стилях сверху, именно он повлияет на отображение документа, так как более специфичен нежели селектор с классом second .

    Правило №3:

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

    Вернемся к первому примеру этой статьи - правило №4:

    div span { text-decoration:underline; } span { text-decoration:none; }

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

    Div span { text-decoration:underline; } body span { text-decoration:none; }

    Теперь селекторы имеют одинаковый вес(0-0-0-2 = 0-0-0-2) касательно специфичности. Второй селектор просто перекроет свойство первого так как описан ниже.

    Уверен, что большинство верстальщиков точно знают 2 первых правила специфичности селекторов про id и style и эта статья не была для них чем-то новым. Но не стоит забывать о двух оставшихся, ведь это может существенно приостановить процесс создания верстки и увеличить время выявления возникшей ошибки.

    Вот и все о чем я хотел рассказать. Надеюсь, что статья была вам интересна.

    --
    Владислав Razor Чапюк, апрель 2009

    В больших проектах при разрастании 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 способа изменения веса селектора, но помните, что все же это хаки и не стоит ими сильно увлекаться.