Галоўная / Артыкулы / Як каскад выбірае пераможця: значэнне, спецыфічнасць і порядак джэраў

Як каскад выбірае пераможця: значэнне, спецыфічнасць і порядак джэраў

Пазірце, як прыграверы рашуяюць суперасабліванне CSS-заяваў, як чытаць специфічнасць як чатыроэлементную параболіку, і чаму правілы hover і !important так часта ствараюць для вас неспакоў.

2447 слоў

Калі калькі правіл CSS нацеленыя на адны элемент і задаюць адну і тую ж власць, прыгледач не можа застосаваць ўсе з іх. Яму патрэбны чыярысты спосаб выбору толькі аднай декларацыі, і гэты процес пояснюе цэлую групу багоў типу «Чаму мой стыль ігнаруецца?». Да канца гэтага кяліку вы зможаце расчысляць спэцыфічнасць селектара, прагназаваць, якая декларацыя будзе пераможнай, і адлагоджваць складныя случаі, такія як правіле :hover, якое ніколі не працюе, без выкарыстоўвання !important.

Якое месца гэта займае ў процесе атрыбутавання

У загальных рысах прыгледач ператварае маркап і стылі на пікселі праз ряд этапаў. HTML парсуецца ў DOM, CSS — у CSSOM, гэтыя два элементы з’едынаюцца ў дрэво атрыбутавання, а пасля процесы распаложэння і накраскі ствараюць тое, што вы бачыце:

HTML
  ↓
DOM
  ↓
CSS
  ↓
CSSOM
  ↓
Render Tree
  ↓
Layout
  ↓
Paint
  ↓
Pixels

У самай сутна CSS-процесу ёсць тры адносныя пытанні. Першае: калі суперначаюць калькі, яка з іх выграе? Другае: калі выбраны паводзяр, да чаго насправды прыведае яго значэнне? Трэцяе: што выходзіць, калі элемент увогуле не мае значэння для якой-небудзь атрыбута? Адказы — каскадаванне (з спецыфічнасцю як ядром), обробка значэнняў і спадчына. Їх часта выкладваюць як незв’язаныя тэмы, але ў браузеры яны ёсць параднымі крокамі адной і той жа задачы. Шэйда зосераджваецца на першам пытанні. Якщо хочаце больш інфармацыі пра тое, што выходзіць пасля обробкі стылю, адзірніце як браузер расфарбоўвае елементы і якая роля у цьму належыць React.

Правіла, калькі і суперначаючыя значэнні

Спачатку трохіць лексыкану. Правіле CSS складаецца з селектара, які следуе за блокам заяв:

.button {
  background-color: blue;
}

У гэтым правіле .button ёсць селектар, background-color: blue; — заява, background-color — атрыбут, а blue — яго значэнне. Значэнне такое, як вы яго напісалі, называецца заявленым значэннем.

Рэальны файл стылю часта містіць калькі заяв для таго ж атрыбута на там самым элементе. Одна правіле можа стосавацца всіх кнопакоў:

button {
  background-color: red;
}

тады як іншыя, можа ў іншым файле, стосуюцца кнопакоў у цалом і адной конкрэтнай кнопцы па ўсім яе id:

button {
  background-color: blue;
}

#submit {
  background-color: green;
}

Якшо адзін элемент <button id="submit"> падае під усі тры правілы, які колер фона ёму трэба? Рашэнне гэтага пытання — задача каскадавання. Ён рашыяе суперсечанні, аналізуючы значэнне кожнага заявлення, спецыфічнасць яго селектара і порядак, у якім заявленні выказаны. Калі вже ўзятае пад увагу значэнне, то зазвычай сама спецыфічнасць вяршыць у выборе рашэння.

У повным описе каскадавання таксама берэцца під увагу мяркаванне шаблона стылю (стандартныя настройкі прыгледача, стылі корыстувальніка, стылі автара), а ў сучасным CSS — роўні каскадавання, заявленыя за дапамогою @layer. Для звычных шаблонаў стылю автара без роўней, значэнне, спецыфічнасць і порядак мяркавання є трыя аспекты, якія трэба врачыць.

Што меркае спецыфічнасць

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

p {
  color: red;
}

селектар класа:

.text {
  color: blue;
}

і селектар id:

#title {
  color: green;
}

Якщо всі трое падаюць на той самы элемент, браузер ранжуе іх за дапамою фіксаванай іерархіі видоў селектараў, ад найсильнейшага да найслабейшага:

Inline styles
     ↓
IDs
     ↓
Classes / pseudo-classes / attributes
     ↓
Elements / pseudo-elements

Калі канкуруючыя декларацыі маюць роўную важнасць, выграе болей спецыфічны селектар. У гэтым случае выграе правілу id, і тэкст будзе зеленым.

Точныя заўважэнні ў верхнем ряду: стылі, заданыя безпосередна ў элементе через атрыбут style, не являюцца селектарамі, і чынныя стандарты расследзяюць іх як окремы крок, який мае прываліднасць над будзь-якай декларацыей автара, базаванай на селектарах. Хоця ў практыцы, як і часта робіцца, ўваходжэнне іх у найвышшую „колонку“ специфічнасці дае той самы рэзультат, уся заступна інструкцыя будзе выкорыстоўваць гэты зручны падход.

Чытанне специфічнасці як чатырох калонак

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

Inline   |   IDs   |   Classes   |   Elements

Для заданага селектара вы пісачыце, сколькі елементаў належыць да кожной категоріі. Селектар, які включае толькі клас, — гэта простейшы случай:

.button {
  background: blue;
}

У ём няма безпосередніх стыліў, няма ідэнтыфікатора id, толькі адны клас і жадных элементаў:

Inline styles → 0
IDs           → 0
Classes       → 1
Elements      → 0

Ёго можна компактна запісаць так:

0, 0, 1, 0

Тепер розглянем болейш адытналі з’яўлення:

nav#main .button div {
  background: green;
}

У ям ёсць адна ідэнтыфікатор (#main), адна класа (.button) і два з’яўленні типу (nav і div); таму яго спецыфічнасць:

0, 1, 1, 2

Ёнколі два з’яўлення, браузер чытае гэтыя туплы з левага на правы, пачынаючы з найболей значныя колонкі. Першая колонка, у якой яны разніцяюцца, вялічы результат, а колонкі па ўзэйдзе важная. Таму адны ідэнтыфікатор пераважае любую колькасць класоў, а адна класа — любую колькасць з’яўленні типу: нема пераносу з адной колонкі ў іншую, таму дзесяць класоў ніколі не „дадаюцца“ да аднаго ідэнтыфікатора. Вам не трэба запамяняць арытметыку; вам трэба памятаць порядак порэвання.

Разбіранне рэальнага канфлікту

Разглядзім кантакт з класам і ідэнтыфайярам. Заўважкі: гэта звычны HTML-маркап, таму выкарыстоўваецца class, а не className з JSX:

<button class="button" id="submit">
  Don't Click
</button>

Тепер заўважкі, што стаяльнік стылю мае правілу класу:

.button {
  background: blue;
}

а таксама калькі іншых правіла, уключаючы селектар типу, дзейнік далекіх наследнікаў і правілу id-plus-class з станам hover:

button {
  background: purple;
}

nav#main .button div {
  background: green;
}

#submit.button:hover {
  background: yellow;
}

Усе яны задаюць параметр background, таму адбываецца конкурэнція. Браузер не выбірае проста тое, што паказана апошней; спачатку ён порыхуе ступеню спецыфічнасці.

У тым блаке ёсць деталь, якую лёгка працязіць: nav#main .button div на самай працэ фармуе div, які знаходзится ўнутрь элемента з класам button, а не саму кнопку. Паколькі об’ектам селектара ёст тыя його элементы, які знаходзяцца справа, гэты правілы ніколі не будзе падходзіць да нашага <button>, незалежна ад яго специфічнасці. Перакананне, чы правілы вообща падходзіць, завжды ёсць першым крокам у дэбагаванні.

Для правілаў, якія падходзяць, параваньце селектар класу:

.button

з селектарам простага типу:

button

У першым ёсць адзін клас, у другым — толькі адзін элемент. Таму:

.button

мае вышэйшую прыорітет:

button

і кнопка ў сінім кольоре, а не фіялетавым, нават якщо правілы для фіялетовага кольору з’яўляецца пазней.

Дадзіце ідэнтыфікатор id:

#submit.button

Id прызначае гэты селектар у вышэйшай колонцы, чым будь-які селектар, створаны толькі з класаў і типаў. Парэйнуючы злева напраўо, колонка z id негайна вяршыць супернікаванне, таму аднае id можа перамагчы селектар, створаны з многа класаў.

Чаму правіле :hover можа нічога не зрабіць

Гэты случай вызывае вельмі большую плутанню пад час дэбагавання. Пачніце з базовага правілы для кантрола:

#submit.button {
  background: red;
}

і правілы hover для таго ж элемента:

#submit.button:hover {
  background: yellow;
}

Правіле hover мае аднае id, адну клас і адную псевдоклас, што дае яму большую спэцыфічнасць, чым базовае правіла, таму падчас наведення курсора кантроль стае жоўтым, як і чакалася.

Тепер уявіце, што іншая частка кодавой базы стылюе той жа кантроль за дапамою набліжна дыяволскага селектара:

nav#main div#container #submit.button {
  background: red;
}

пры тым, як правіле hover застаецца незменным:

#submit.button:hover {
  background: yellow;
}

Правіле hover яшчэ мае псевдоклас:

:hover

Псевдокласы таксама лічуюцца ў столбце класаў. Але гэта даўа толькі адну баловую елементару на рэжым класа. Дзяржак выбору, які ўключае тры ідэнтыфікаторы, перамагае дзяржак правіла hover, які мае толькі адны ідэнтыфікатор, ўжо на стадії параболення ідэнтыфікатораў, прычаму класы ўжо не параболююцца. Рэзультатам яе ёсць правіло :hover, якое синтаксычна ідеальна, падходзіць элементу, але так і не зменяе нічога на экране.

Урок заключаецца ў тым, што калі станы взаімадзеяння здаюцца несправнымі, псевдоклас рэдка бывае прычыной. Настоямая проблема зазвычай заключаецца ў тым, што якая-небудзь іншая декларацыя є болей спецыфічной. Інструменты разработчика прыгледу браузера дапамагаюць гэта пазначыць: панель Стыліў перыядае кожнае правіло, якое падходзіць, і ставя прапэскі пад тымі декларацыямі, якія праспалі, чым точна паказваецца, які самэўбірач перамага ваш.

Равенства разбіваюцца за порядкам выказвання

Інодзе два самэўбірачы маюць ідэнтычную спецыфічнасць. Возьмем два правіла з аднаковым самэўбірачам класа:

.button {
  background: red;
}
.button {
  background: blue;
}

Яны ўсё такі ж спецыфічныя і ўсё такі ж важлівыя, таму ні першы, ні другі рэвень не можа прымець рашэння. Тады браузер пераходзіць да порядку выказвання: перамагае тая декларацыя, якая выказана пазней. У такім порядку:

.button {
  background: red;
}
.button {
  background: blue;
}

кнапка ў канцы стае сіней.

Увесь процес прымэчання рашэння можна адобразіць як серыю спосабаў для развязвання таварышства:

Importance
    ↓
Specificity
    ↓
Source Order

Кожны рэвень адзін раз прыменяецца, якщо паканальны не змогаў вылучыць пераможца. Спачатку дывіцца на важлівасць; якщо ёй таксама таварышаюць, дывіцца на спецыфічнасць; якщо і ў гэтым случае таварышаюць, перамагае апошняя декларацыя.

Кост выкарыстання !important

Практычна кожны разработчык хоць адназначы выкарыстаў гэты спосаб развязвання проблем:

color: red !important;

Дадзенне !important падышае значэння важнасці декларацыі. Пакалькі важнасць пераглядаецца раней за спэцыфічнасцю, декларацыя, пазначаная такім чынам, можа перамогчы іншую декларацыю з набагато вышэй спэцыфічнасцю. Напрыклад:

.button {
  background: purple !important;
}

яна пераможае звычную декларацыю ў дужа дзейнай селектары. Калі две декларацыі з !important конкуруюць, браузер зноў пачынае пораўнюваць ўжо спэцыфічнасць, а потым і ўпорядкаванне.

Самэ гэтая моц і ўчыняе яе рызыкаванай. Звычны цикл дэбагавання выглядае так:

"My style isn't working."
          ↓
"Let's increase the specificity."
          ↓
"Still not working."
          ↓
"Let's add !important."
          ↓
    "It works!"

Стыл нарэшце практыкуецца, але асновны канфлікт не зник; ён пераходзіць да наступнага чалавека, якому трэба змяніць гэтыя настройкі, і той тепер мусі борацца зі своім сопственным !important. Калі такіх канфліктов стае больш, стаяць усё сложней разумець структуру файла з настройкамі стылю. Вважайце !important апошнім выкарыстоўваным спосабам, а раптовую патрэбу ў яму — сігналам таго, што CSS, вероятна, патрэбуе рефакторавання.

Перш чым пачаць пісаць:

!important

задайце болей корыстнае пытанне: чаму мая декларацыя не перамагае? Потым пераканайцеся ў рангах па следуючым порядку:

  • Чы є конкуруючая декларацыя болей важлівай?
  • Чы є конкуруючы селектар болей спецыфічным?
  • Чы конкуруючая декларацыя з’являецца пазней у коде?

Існуюць легітымныя способы ўжыцця, такія як класы-функцыі, якія павінны завжды праўляць стылі, або перакрыцчые інлайн-стылі, якія вводзяцца трэцьімі сторонамі і якія нельга зменіць, але ўжыццё іх павінна быць цялеспрямованым, а не інстынктыўным.

Стварэнне селектараў, якія ефектываўна праўляюць стылі

Хутчэй за ўсё, тут важліва можлівасць падтрымкі коду. Калі стыль не праўляецца, бывае спакуса робіць селектар все дыяўольскій, пакуль ён не запрацюе:

body div section nav ul li a.button {
  color: red;
}

Это працуе, але кожны дадатковы элемент падвышае стандарт для будучых перакрыцчы, прыяўляе стылі да конкрэтнай структуры DOM і робіць файл з правіламі CSS менш чытаемым. Уместа таго, каб спытацца, як за ўсю цену змусіць селектар праўляць стылі, трэба спытацца, як організаваць CSS так, каб намечаная декларацыя сама праўляла стылі. Калі большасць селектараў апісваецца за дапамогою аднаго класу, калі для стылізацыі не вжываюцца ідэны, а болей спецыфічныя перакрыцчы размешчаюцца поблізу правілаў, якія ўжо зменяюцься, гэта дапамагае.

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

Выкарыстоўванне порядку выклікання сортавакаў з стылямі трэціх сторон

Порядак выклікання сортавакаў стае практычным інструментам, калі вы спаўнаеце своія стылі з сортавакамі для перазначэння настройкаў або стылямі трэціх сторон. Гэтыя файлы зазвычай задаюць настройкі для распашчатых элементаў, і вы хочаце, каб вашы правілы ўсунулі іх. Выкліканне вашага сортавака пасля іх прыменяе гэта проста:

<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="style.css">

Калі вашы селектары маюць тую ж специфічнасць, як і селектары бібліятэкі, пасляўшый файл мае перавагу, таму размішчэнне style.css пасля reset.css дазволяе вашым заявам вступіць у силу без збільшэння колькасці селектараў.

Залежнасць ад порядку таксавана мае свою цену. Якщо хтось пазней зноў запрашае тэгі <link> або элементы, якія імпортуюцься ў точце входу бандлера, стылі можаць таямна змяніцца. Як толькі можаце, выбірайце правілы, специфічнасць каторых чыста паказвае, калі які варыянт будзе выкарыстоўвацца, і вжывайце порядак выканання як фінальны критэрый, для чаго ён і быў створаны. Слойвы каскадавання, якія падтрымваюцься вашыма цэльвымі браузерамі, — это болей ясны спосаб выразыць ідею „бібліятэка першая, нашы стылі пасля“.

Пасля выбранага варыянта: каскадаваная значэння

У гэты момент на першы вопыт ўжо ёсць адпаведнае адказа. Браузер збірае ўсе заявы пра даную властывасць, апраноўвае ўвагу да значэння, потым — до специфічнасці, а пасля — да порядку выканання, і выбирае одну з яных. Гэтае выбранае значэння называецца каскадаваная значэння.

Аднак робота яшчэ не завершана. Падазроўваю, што выбраная заява ёсць такой:

width: 66%;

Такі відсоткі не можна намалюваць безпасова; прыгледар все равна должен іх обрабатваць, вырахаваючы ўсё на адной з блакоў, куды яны паўтараюцца, ўбачыць рэальную дужыну. Гэтая обработка значэння, а таксама спадчына для якостей, якіх зовсім няма ў декларацыі, являецца наступным этапам ператварэння CSS у пікселі.

Ключовыя моманты

  • Система каскадавання рашае суперсечанні правіл у фіксаванай парады: спачатку значэнне важлівасці, потым специфічнасць, а пасля — порядак джэрела.
  • Специфічнасць вырахоўваецца па чатыро столбцамі (inline, ідэнтыфікаторы, класы/псевдокласы/атрыбуты, элементы/псевдоэлементы) і чытаецца з левага на правы; значэння з нижэйшых столбцоў ніколі не пераходзяць у вышэйшыя.
  • Перад пораўнанням специфічнасці правіла, трэба пераканацца, што яно дзейсна падходзіць элементу; самыя правыя часткі селектара ўсё, на шта воны намячаны.
  • Правіла з псевдокласамі :hover або іншымі даюць толькі вазу на рэгламентаў класа і можу быть перакананы болей спецыфічным базовым правілам.
  • !important перамагае за рахунак змены важнасці, а не вырашэння канфлікту; іспользуйце яго рашуча і адзіночна.
  • Вярніцеся да короткіх селектараў, базаваных на класах, і до логічнага порядку стайл-шытов, каб намечаная декларацыя перамагала без эскаляціі.