Специфичность
Специфичность — это алгоритм, используемый браузерами для определения декларации CSS, которая наиболее релевантна для элемента. Это, в свою очередь, определяет значение свойства, которое необходимо применить к элементу. Алгоритм специфичности рассчитывает вес CSS селектора, чтобы определить, какая из конкурирующих деклараций CSS применяется к элементу.
Примечание: Браузеры учитывают специфичность после определения происхождения и важности каскада. Другими словами, для конкурирующих деклараций свойств специфичность имеет значение и сравнивается только между селекторами из одного происхождения каскада и слоя, который имеет приоритет для свойства. Близость области видимости и порядок появления становятся релевантными, когда специфичности селекторов конкурирующих деклараций в слое каскада с приоритетом равны.
Как рассчитывается специфичность?
Специфичность — это алгоритм, который рассчитывает вес, применяемый к данной декларации CSS. Вес определяется количеством селекторов каждой категории весов в селекторе, соответствующем элементу (или псевдоэлементу). Если есть две или более декларации, предоставляющие разные значения свойств для одного и того же элемента, значение декларации в блоке стилей, имеющем соответствующий селектор с наибольшим алгоритмическим весом, применяется.
Алгоритм специфичности в основном представляет собой трёхколонное значение трёх категорий или весов — ID, CLASS и TYPE — соответствующие трём типам селекторов. Значение представляет собой количество компонентов селектора в каждой категории весов и записывается как ID - CLASS - TYPE. Три столбца создаются путём подсчёта количества компонентов селектора для каждой категории весов селекторов, которые соответствуют элементу.
Категории весов селекторов
Категории весов селекторов перечислены здесь в порядке убывания специфичности:
- Столбец ID
-
Включает только селекторы ID, такие как
#example. Для каждого ID в сопоставляющем селекторе добавляется 1-0-0 к значению веса. - Столбец CLASS
-
Включает селекторы классов, такие как
.myClass, селекторы атрибутов, такие как[type="radio"]и[lang|="fr"], и псевдоклассы, такие как:hover,:nth-of-type(3n), и:required. Для каждого класса, селектора атрибута или псевдокласса в сопоставляющем селекторе добавляется 0-1-0 к значению веса. - Столбец TYPE
-
Включает селекторы типов, такие как
p,h1, иtd, и псевдоэлементы, такие как::before,::placeholder, и все остальные селекторы с обозначением с двумя двоеточиями. Для каждого типа или псевдоэлемента в сопоставляющем селекторе добавляется 0-0-1 к значению веса. - Нет значения
-
Универсальный селектор (
*) и псевдокласс:where()и его параметры не учитываются при расчёте веса, поэтому их значение равно 0-0-0, но они соответствуют элементам. Эти селекторы не влияют на значение веса специфичности.
Комбинаторы, такие как +, >, ~, " " и ||, могут сделать селектор более специфичным по отношению к тому, что выбирается, но они не добавляют никакой величины к весу специфичности.
Комбинатор вложенности & не добавляет веса специфичности, но вложенные правила добавляют. С точки зрения специфичности и функциональности вложенность очень похожа на псевдокласс :is().
Так же как и вложенность, псевдоклассы :is(), :has() и отрицания (:not()) сами по себе не добавляют веса. Однако параметры в этих селекторах добавляют. Вес специфичности каждого исходит из параметра селектора в списке селекторов с наибольшей специфичностью. Аналогично, с вложенными селекторами, вес специфичности, добавляемый компонентом вложенного селектора, — это селектор в списке вложенных селекторов, разделённых запятыми, с наибольшей специфичностью.
:not(), :is(), :has() и исключения вложенности CSS обсуждаются ниже.
Сопоставляющий селектор
Вес специфичности исходит от сопоставляющего селектора. Рассмотрим этот CSS селектор с тремя селекторами, разделёнными запятыми, в качестве примера:
[type="password"],
input:focus,
:root #myApp input:required {
color: blue;
}
Селектор [type="password"] в вышеуказанном списке селекторов, с весом специфичности 0-1-0, применяет декларацию color: blue ко всем типам входных данных пароля.
Все входы, независимо от типа, при получении фокуса, соответствуют второму селектору в списке, input:focus, с весом специфичности 0-1-1; этот вес состоит из псевдокласса :focus (0-1-0) и типа input (0-0-1). Если у элемента ввода пароля есть фокус, он будет соответствовать input:focus, и вес специфичности для декларации стиля color: blue будет 0-1-1. Когда у элемента ввода пароля нет фокуса, вес специфичности остаётся равным 0-1-0.
Специфичность для требуемого элемента ввода, вложенного в элемент с атрибутом id="myApp", составляет 1-2-1, на основе одного ID, двух псевдоклассов и одного типа элемента.
Если тип элемента ввода пароля с required вложен в элемент с id="myApp" установленным, вес специфичности будет 1-2-1, на основе одного ID, двух псевдоклассов и одного типа элемента, имеет ли он фокус или нет. Почему вес специфичности 1-2-1 а не 0-1-1 или 0-1-0 в данном случае? Потому что вес специфичности исходит от сопоставляющего селектора с наибольшим весом специфичности. Вес определяется путём сравнения значений в трёх столбцах, слева направо.
[type="password"] /* 0-1-0 */ input:focus /* 0-1-1 */ :root #myApp input:required /* 1-2-1 */
Сравнение трёх столбцов
После определения значений специфичности соответствующих селекторов, количество компонентов селектора в каждом столбце сравниваются слева направо.
#myElement {
color: green; /* 1-0-0 - WINS!! */
}
.bodyClass .sectionClass .parentClass [id="myElement"] {
color: yellow; /* 0-4-0 */
}
Первый столбец — значение компонента ID, то есть количество ID в каждом селекторе. Сравниваются числа в столбцах ID конкурирующих селекторов. Селектор с большим значением в столбце ID выигрывает независимо от значений в других столбцах. В приведённом выше примере, даже если у жёлтого селектора больше компонентов в общем, имеет значение только значение первого столбца.
Если число в столбцах ID конкурирующих селекторов одинаково, сравнивается следующий столбец, CLASS, как показано ниже.
#myElement {
color: yellow; /* 1-0-0 */
}
#myApp [id="myElement"] {
color: green; /* 1-1-0 - WINS!! */
}
Столбец CLASS — это счётчик имён классов, селекторов атрибутов и псевдоклассов в селекторе. Когда значение столбца ID одинаково, селектор с большим значением в столбце CLASS выигрывает, независимо от значения в столбце TYPE. Это показано в примере ниже.
:root input {
color: green; /* 0-1-1 - WINS because CLASS column is greater */
}
html body main input {
color: yellow; /* 0-0-4 */
}
Если значения в столбцах CLASS и ID в конкурирующих селекторах одинаковы, столбец TYPE становится релевантным. Столбец TYPE — это количество типов элементов и псевдоэлементов в селекторе. Когда первые два столбца имеют одинаковые значения, выигрывает селектор с большим числом в столбце TYPE.
Если у конкурирующих селекторов одинаковые значения во всех трёх столбцах, применяется правило близости, при котором последний объявленный стиль имеет приоритет.
input.myClass {
color: yellow; /* 0-1-1 */
}
:root input {
color: green; /* 0-1-1 WINS because it comes later */
}
Исключения для :is(), :not(), :has() и вложенности CSS
Псевдокласс :is() для совпадения со всеми элементами, псевдокласс :has() для проверки наличия элементов и псевдокласс :not() для отрицания не учитываются при расчете специфичности. Они сами не добавляют веса к уравнению специфичности. Однако параметры селектора, передаваемые в скобки псевдокласса, являются частью алгоритма специфичности; вес псевдоклассов matches-any и negation в расчете значения специфичности — это вес параметра веса.
p {
/* 0-0-1 */
}
:is(p) {
/* 0-0-1 */
}
h2:nth-last-of-type(n + 2) {
/* 0-1-1 */
}
h2:has(~ h2) {
/* 0-0-2 */
}
div.outer p {
/* 0-1-2 */
}
div:not(.inner) p {
/* 0-1-2 */
}
Обратите внимание, что в приведенной выше паре CSS вес специфичности, предоставляемый псевдоклассами :is(), :has() и :not(), — это значение параметра селектора, а не псевдокласса.
Все три этих псевдокласса принимают в качестве параметра сложные списки селекторов, список селекторов, разделенных запятыми. Этот функционал можно использовать для повышения специфичности селектора:
:is(p, #fakeId) {
/* 1-0-0 */
}
h1:has(+ h2, > #fakeId) {
/* 1-0-1 */
}
p:not(#fakeId) {
/* 1-0-1 */
}
div:not(.inner, #fakeId) p {
/* 1-0-2 */
}
В приведенном выше блоке CSS мы включили #fakeId в селекторы. Этот #fakeId добавляет 1-0-0 к весу специфичности каждого абзаца.
При создании сложных списков селекторов с помощью вложенности CSS это поведение идентично псевдоклассу :is().
p,
#fakeId {
span {
/* 1-0-1 */
}
}
В приведенном выше блоке кода сложный селектор p, #fakeId берет специфичность из #fakeId и также из span, что создает специфичность 1-0-1 для обоих p span и #fakeId span. Это эквивалентная специфичность селектора :is(p, #fakeId) span.
В целом, следует стремиться минимизировать специфичность, но если по какой-либо причине необходимо повысить специфичность элемента, эти три псевдокласса могут помочь.
a:not(#fakeId#fakeId#fakeID) {
color: blue; /* 3-0-1 */
}
В этом примере все ссылки будут синими, за исключением случаев переопределения объявлением ссылки с 3 или более идентификаторами, значением цвета, соответствующим a или если ссылка содержит флаг !important или объявление цвета в стиле встраивания.
Встроенные стили
Встроенные стили, добавленные к элементу (например, style="font-weight: bold;"), всегда переопределяют любые обычные стили в стиле автора, и поэтому могут считаться обладающими наибольшей специфичностью. Представьте, что встроенные стили имеют вес специфичности 1-0-0-0.
Единственный способ переопределить встроенные стили — использовать !important.
Многие JavaScript-фреймворки и библиотеки добавляют встроенные стили. Использование !important с очень целевым селектором, таким как селектор атрибута, использующий встроенный стиль, является одним из способов переопределения этих встроенных стилей.
<p style="color: purple">…</p>
p[style*="purple"] {
color: rebeccapurple !important;
}
Убедитесь, что к каждому включению флага important прикреплен комментарий, чтобы разработчики понимали, почему был использован этот антишаблон CSS.
Исключение для !important
Объявления CSS, помеченные как важные, переопределяют любые другие объявления в том же слое каскада и источнике. Хотя технически !important не имеет отношения к специфичности, оно взаимодействует непосредственно со специфичностью и каскадом. Оно меняет порядок каскада стилей.
Если объявления из одного источника и слоя каскада конфликтуют, и значение одного свойства имеет флаг !important , важное объявление применяется независимо от специфичности. Если конфликтующие объявления из одного источника и слоя каскада с флагом !important применяются к одному элементу, применяется объявление с большей специфичностью.
Использование !important для переопределения специфичности считается плохой практикой и должно быть избегаемо в этих целях. Понимание и эффективное использование специфичности и каскада могут устранить любые потребности в флаге !important.
Вместо использования !important для переопределения внешнего CSS (из внешних библиотек, таких как Bootstrap или normalize.css), импортируйте сторонние скрипты непосредственно в слои каскада. Если вам необходимо использовать !important в вашем CSS, добавьте комментарий к его использованию, чтобы будущие разработчики знали, почему объявление было помечено как важное, и не переопределять его. Но обязательно избегайте !important при написании плагинов или фреймворков, которые другие разработчики должны будут интегрировать, не имея возможности контролировать.
Исключение для :where()
Псевдокласс корректировки специфичности :where() всегда заменяет свою специфичность нулем, 0-0-0. Это позволяет создавать очень специфические CSS-селекторы, нацеленные на элемент, не увеличивая специфичности.
При создании стороннего CSS для использования разработчиками, у которых нет доступа к редактированию вашего CSS, считается хорошей практикой создавать CSS с минимально возможной специфичностью. Например, если ваша тема включает следующий CSS:
:where(#defaultTheme) a {
/* 0-0-1 */
color: red;
}
Тогда разработчик, внедряющий виджет, может легко переопределить цвет ссылки, используя только селекторы типа.
footer a {
/* 0-0-2 */
color: blue;
}
Как блоки области действия влияют на специфичность
Включение правила внутри блока области действия не влияет на специфичность его селектора, независимо от селекторов, используемых внутри корня и границы области действия. Например:
@scope (.article-body) {
/* img has a specificity of 0-0-1, as expected */
img { ... }
}
Однако, если вы решите явно добавить псевдокласс :scope к своим селекторам области действия, вам нужно будет учитывать его при расчете их специфичности. :scope, как и все обычные псевдоклассы, имеет специфичность 0-1-0. Например:
@scope (.article-body) {
/* :scope img has a specificity of 0-1-0 + 0-0-1 = 0-1-1 */
:scope img { ... }
}
При использовании селектора & внутри блока области действия @scope , & представляет собой селектор корня области действия; он внутренне переписывается в этот селектор, заключенный в селектор :is(). Таким образом, например, в:
@scope (figure, #primary) {
& img { ... }
}
& img эквивалентно :is(figure, #primary) img.
Поскольку :is() берет специфичность своего самого специфичного аргумента (#primary в данном случае), специфичность селектора области действия & img составляет поэтому 1-0-0 + 0-0-1 = 1-0-1.
Советы по решению проблем с специфичностью
Вместо использования !important, рассмотрите использование слоев каскада и использование низкой специфичности в вашем CSS, чтобы стили легко переопределялись более специфичными правилами. Использование семантического HTML помогает обеспечить якоря для применения стилей.
Создание специфичных селекторов с добавлением и без добавления специфичности
Указав раздел документа, который вы форматируете, перед элементом, который выбираете, правило становится более специфичным. В зависимости от того, как вы это сделаете, вы можете добавить немного, много или вообще не добавить специфичности, как показано ниже:
<main id="myContent"> <h1>Text</h1> </main>
#myContent h1 {
color: green; /* 1-0-1 */
}
[id="myContent"] h1 {
color: yellow; /* 0-1-1 */
}
:where(#myContent) h1 {
color: blue; /* 0-0-1 */
}
В любом порядке заголовок будет зеленым, потому что это наиболее специфичное правило.
Сокращение специфичности ID
Специфичность основана на форме селектора. Включение id элемента в качестве селектора атрибута вместо селектора ID — хороший способ сделать элемент более специфичным, не добавляя излишней специфичности. В предыдущем примере селектор [id="myContent"] считается селектором атрибута для определения специфичности селектора, несмотря на то, что он выбирает ID.
Вы также можете включить id или любую часть селектора в качестве параметра в псевдоклассе корректировки специфичности :where(), если вам нужно сделать селектор более специфичным, но не хотите добавлять специфичности вообще.
Повышение специфичности путем дублирования селектора
В качестве специального случая для повышения специфичности вы можете дублировать веса из столбцов CLASS или ID. Дублирование селекторов id, class, псевдоклассов или атрибутов внутри составного селектора повысит специфичность при переопределении очень специфичных селекторов, на которые у вас нет контроля.
#myId#myId#myId span {
/* 3-0-1 */
}
.myClass.myClass.myClass span {
/* 0-3-1 */
}
Используйте это с осторожностью, если вообще используете. Если вы используете дублирование селекторов, всегда комментируйте свой CSS.
Используя :is() и :not() (а также :has() ), вы можете повысить специфичность, даже если не можете добавить id к родительскому элементу:
:not(#fakeID#fakeId#fakeID) span {
/* 3-0-1 */
}
:is(#fakeID#fakeId#fakeID, span) {
/* 3-0-0 */
}
Преимущество над CSS сторонних разработчиков
Использование слоёв каскадирования — стандартный способ, позволяющий одному набору стилей иметь приоритет над другим; слои каскадирования обеспечивают это без использования специфичности! Обычные (не важные) авторские стили, импортированные в слои каскадирования, имеют меньший приоритет, чем неслоированные авторские стили.
Если стили поступают из таблицы стилей, которую вы не можете редактировать или не понимаете, и вам нужно переопределить стили, стратегия заключается в импорте стилей, которые вы не контролируете, в слой каскадирования. Стили в последующих слоях имеют приоритет, при этом неслоированные стили имеют приоритет над всеми слоистыми стилями из того же источника.
Когда два селектора из разных слоёв соответствуют одному и тому же элементу, приоритет отдаётся источнику и важности; специфичность селектора в проигравшей таблице стилей не имеет значения.
<style>
@import TW.css layer();
p,
p * {
font-size: 1rem;
}
</style>
В приведённом выше примере весь текст абзаца, включая вложенное содержимое, будет 1rem независимо от количества имён классов в абзацах, соответствующих таблице стилей TW.
Избегание и переопределение !important
Лучший подход — не использовать !important. Объяснения специфичности выше должны помочь избежать использования этого флага и полностью удалить его при обнаружении.
Чтобы устранить видимую необходимость в !important, можно сделать следующее:
- Увеличить специфичность селектора ранее
!importantобъявления, чтобы оно было больше, чем другие объявления - Наделить его той же специфичностью и поместить его после объявления, которое он должен переопределить
- Уменьшить специфичность селектора, который вы пытаетесь переопределить.
Все эти методы описаны в предыдущих разделах.
Если вы не можете удалить !important флаги из таблицы стилей автора, единственным решением для переопределения важных стилей является использование !important. Создание слоя каскадирования важных объявлений переопределений является отличным решением. Два способа сделать это:
Способ 1
- Создайте отдельную, короткую таблицу стилей, содержащую только важные объявления, которые конкретно переопределяют любые важные объявления, которые вы не смогли удалить.
- Импортируйте эту таблицу стилей в качестве первого импорта в ваш CSS, используя
layer(), включая@importутверждение, перед подключением к другим таблицам стилей. Это необходимо для того, чтобы важные переопределения импортировались в качестве первого слоя.
<style> @import importantOverrides.css layer(); </style>
Способ 2
-
В начале ваших объявлений стилей создайте именованный слой каскадирования, как показано ниже:
@layer importantOverrides;
-
Каждый раз, когда вам нужно переопределить важное объявление, объявите его в рамках именованного слоя. Объявляйте важные правила только внутри слоя.
[id="myElement"] p { /* normal styles here */ } @layer importantOverrides { [id="myElement"] p { /* important style here */ } }
Специфичность селектора важного стиля внутри слоя может быть низкой, если только он соответствует элементу, который вы пытаетесь переопределить. Обычные слои должны быть объявлены за пределами слоя, потому что слоистые стили имеют меньший приоритет, чем неслоистые стили.
Безразличие к близости элементов в дереве
Близость элемента к другим элементам, на которые ссылается данный селектор, не влияет на специфичность.
body h1 {
color: green;
}
html h1 {
color: purple;
}
Элементы <h1> будут фиолетовыми, потому что когда объявления имеют одинаковую специфичность, последний объявленный селектор имеет приоритет.
Элементы, на которые непосредственно нацелены стили, против унаследованных стилей
Стили для непосредственно нацеленного элемента всегда имеют приоритет над унаследованными стилями, независимо от специфичности унаследованного правила. Учитывая следующий CSS и HTML:
#parent {
color: green;
}
h1 {
color: purple;
}
<html lang="en">
<body id="parent">
<h1>Here is a title!</h1>
</body>
</html>
Элемент h1 будет фиолетовым, потому что селектор h1 нацелен на элемент непосредственно, а зелёный цвет унаследован от #parent объявлений.
Примеры
В следующем CSS у нас есть три селектора, нацеленные на <input> элементы для установки цвета. Для данного элемента ввода приоритет имеет объявление цвета с наибольшим весом соответствующего селектора:
#myElement input.myClass {
color: red;
} /* 1-1-1 */
input[type="password"]:required {
color: blue;
} /* 0-2-1 */
html body main input {
color: green;
} /* 0-0-4 */
Если все вышеупомянутые селекторы нацелены на один и тот же элемент ввода, элемент ввода будет красным, так как первое объявление имеет наибольшее значение в столбце ID.
Последний селектор содержит четыре компонента TYPE. Хотя он имеет наибольшее целое значение, независимо от того, сколько элементов и псевдоэлементов включено, даже если их было 150, компоненты TYPE никогда не имеют приоритета над компонентами CLASS. Значения столбцов сравниваются слева направо, когда значения столбцов равны.
Если бы мы преобразовали селектор id в примере кода выше в селектор атрибута, первые два селектора имели бы одинаковую специфичность, как показано ниже:
[id="myElement"] input.myClass {
color: red;
} /* 0-2-1 */
input[type="password"]:required {
color: blue;
} /* 0-2-1 */
Когда несколько объявлений имеют одинаковую специфичность, применяется последнее объявление, найденное в CSS, к элементу. Если оба селектора соответствуют тому же <input>, цвет будет синим.
Дополнительные примечания
Несколько моментов, которые следует помнить о специфичности:
-
Специфичность применяется только тогда, когда один и тот же элемент нацелен на несколько объявлений в одном слое каскадирования или источнике. Специфичность имеет значение только для объявлений с одинаковой важностью и одинаковым происхождением и слоем каскадирования. Если соответствующие селекторы находятся в разных источниках, каскад определяет, какое объявление имеет приоритет.
-
Когда два селектора в одном слое каскадирования и источнике имеют одинаковую специфичность, вычисляется близость области; правила с наименьшей близостью области выигрывают. См. Как
@scopeразрешаются конфликты для получения более подробной информации и примера. -
Если близость области также одинакова для обоих селекторов, вступает в игру порядок источников. При равенстве всего остального, последний селектор выигрывает.
-
В соответствии с правилами CSS, элементы, на которые непосредственно нацелены стили, всегда имеют приоритет над правилами, которые элемент наследует от своего предка.
-
Близость элементов в дереве документа не влияет на специфичность.
Спецификации
| Спецификация |
|---|
| Селекторы уровня 4 # specificity-rules |
См. также
- "Специфичность" в "Каскадирование и наследование"
- SpeciFISHity
- Калькулятор специфичности: интерактивный веб-сайт для тестирования и понимания собственных правил CSS
- Упражнение ID-CLASS-TYPE — викторина по специфичности
- Синтаксис CSS руководство
- Модуль синтаксиса CSS
- Обработка ошибок CSS
- Правила @
- Наследование
- Начальное, вычисленное, используемое и фактическое значения
- Синтаксис определения значений
- Основы: CSS-каскад
- Основы: слои каскадирования
- Модуль CSS-каскада и наследования
- Модуль вложенности CSS
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity