ספציפיות

The CSS Podcast - 003: Specificity

נניח שאתם עובדים עם קוד ה-HTML וה-CSS הבא:

<button class="branding">Hello, Specificity!</button>
.branding {
  color: blue;
}

button {
  color: red;
}

בדוגמה הזו יש שני כללים שמטרגטים את אותו אלמנט. כל כלל מכיל הצהרה שמנסה להגדיר את הצבע של הכפתור: אחד מנסה לצבוע את הכפתור באדום והשני מנסה לצבוע אותו בכחול. איזו הצהרה מוחלת על הרכיב?

הבנת אלגוריתם הספציפיות של CSS היא המפתח להבנת האופן שבו CSS מחליט בין הצהרות מתחרות.

מידת הספציפיות היא אחד השלבים הייחודיים של ה-cascade, שעליו דיברנו במודול הקודם, בנושא ה-cascade.

דירוג הספציפיות

כל כלל בחירה במקור מקבל ניקוד. אפשר לחשוב על הספציפיות כציון כולל, וכל סוג של סלקטור מקבל נקודות לציון הזה. ההצהרות מהכללים עם הספציפיות הכי גבוהה הן אלה שמובילות.

בפרויקט אמיתי, כדי להשתמש בספציפיות בצורה יעילה, צריך לוודא שכללי ה-CSS שרוצים להחיל אכן חלים,ובמקביל לשמור על ערכים נמוכים כדי למנוע מורכבות. הספציפיות צריכה להיות ברמה הנדרשת בלבד, ולא ברמה הגבוהה ביותר שאפשר. יכול להיות שבעתיד יהיה צורך להחיל CSS חשוב יותר. אם תבחרו את רמת הספציפיות הכי גבוהה, יהיה קשה לבצע את המשימה.

הספציפיות היא לא מספר עשרוני אלא טריאדה שמורכבת משלושה רכיבים: A, B ו-C.

  • A: ספציפיות כמו מזהה
  • B: ספציפיות כמו של מחלקה
  • C: ספציפיות כמו של רכיב

היא מיוצגת לעיתים קרובות באמצעות הסימון (A,B,C). לדוגמה: (1,0,2). גם הסימון החלופי A-B-C נפוץ.

דיאגרמה שבה מוצגים שלושת הרכיבים של הספציפיות (A,‏ B,‏ C). לכל רכיב, בתרשים מוצג מה הוא מייצג וכמה דוגמאות של סלקטורים שמשפיעים עליו.
תרשים שממחיש על איזה רכיב של ספציפיות משפיעים סלקטורים שונים.

איך משווים בין רמות ספציפיות

כדי להשוות בין רמות הספציפיות, משווים בין שלושת הרכיבים לפי הסדר הבא: רמת הספציפיות עם ערך A גדול יותר היא ספציפית יותר. אם שני ערכי A זהים, רמת הספציפיות עם ערך B גדול יותר היא ספציפית יותר. אם גם שני ערכי B זהים, רמת הספציפיות עם ערך C גדול יותר היא ספציפית יותר. אם כל הערכים זהים, רמות הספציפיות שוות.

לדוגמה, (1,0,0) נחשב ספציפי יותר מ-(0,4,3) כי הערך של A ב-(1,0,0) (שהוא 1) גדול מהערך של A ב-(0,4,3) (שהוא 0).

הסלקטורים משפיעים על הספציפיות

כל חלק בשלישיית הספציפיות מתחיל בערך 0, ולכן הספציפיות שמוגדרת כברירת מחדל היא (0,0,0). כל חלק בסלקטור מגדיל את הספציפיות, ובהתאם לסוג הסלקטור, מגדיל את הערך של A, B או C.

בורר אוניברסלי

סלקטור אוניברסלי (*) לא מוסיף ספציפיות, והערך שלו נשאר הספציפיות הראשונית של (0,0,0).

* {
  color: red;
}

בורר של רכיב או פסאודו-רכיב

סלקטור של רכיב (סוג) או של פסאודו-רכיב מוסיף ספציפיות שדומה לזו של רכיב, וכך מגדיל את הרכיב C ב-1.

בדוגמאות הבאות, רמת הספציפיות הכוללת היא (0,0,1).

בורר סוג

div {
  color: red;
}

בורר פסאודו-אלמנטים

::selection {
  color: red;
}

בורר מחלקה, בורר פסאודו-מחלקה או בורר מאפיינים

סלקטור של class,‏ pseudo-class או attribute מוסיף ספציפיות שדומה ל-class, שמעלה את הרכיב B ב-1.

בדוגמאות הבאות, רמת הספציפיות היא (0,1,0).

בורר הכיתות

.my-class {
  color: red;
}

בורר פסאודו-מחלקות

:hover {
  color: red;
}

בורר מאפיינים

[href='#'] {
  color: red;
}

בורר מזהים

בורר מזהה מוסיף ספציפיות שדומה למזהה, שמגדילה את רכיב A ב-1, כל עוד משתמשים בבורר מזהה (#myID) ולא בבורר מאפיינים ([id="myID"]).

בדוגמה הבאה, הספציפיות היא (1,0,0)

#myID {
  color: red;
}

בוררים אחרים

יש הרבה סלקטורים ב-CSS. לא כל המילים האלה מוסיפות ספציפיות. לדוגמה, פסאודו-הסיווג :not() לא מוסיף כלום לחישוב הספציפיות.

הספציפיות של פסאודו-הסלקטור :not() מוחלפת בספציפיות של הסלקטור הספציפי ביותר בארגומנט הסלקטורים המופרד בפסיקים; הספציפיות זהה לספציפיות שהייתה מתקבלת אם היה כתוב :not(:is(argument)).

div:not(.my-class) {
  color: red;
}

גם פסאודו-המחלקות :is() לא מוסיפות לחישוב הספציפיות. בדומה ל-:not(), היא מקבלת את הספציפיות של הארגומנט הספציפי ביותר שלה.

:is(h1, h2, h3) {
  color: blue;
}

לדוגמה הזו יש ספציפיות של (0,0,1) כי יש בה רק סוג אחד של סלקטור (type).

אם מוסיפים id, הספציפיות עולה ל-(1,0,0).

:is(h1, h2, h3, #my-heading) {
  color: blue;
}

הפסאודו-קלאס :where() שונה. לא משנה כמה ספציפיים הארגומנטים שלו, תמיד יש לו ספציפיות של (0,0,0).

:where(h1, h2, h3, #my-heading) {
  color: blue;
}

הסגנונות עם הספציפיות הנמוכה שמוחלים באמצעות :where() מאפשרים לכם להחליף את הסגנונות האלה באמצעות בוררים בסיסיים בהמשך גיליון הסגנונות:

:where(#my-content) {
   color: red;
}

p {
   color: blue;
}

למרות של-:where() יש id ברשימת הארגומנטים שלו, הוא עדיין מוגדר עם ספציפיות של (0, 0, 0), ולכן הסלקטור הבסיסי p מבטל אותו והטקסט יהיה blue.

בדיקה של רמת ההבנה שלך

בודקים את הידע בנוגע לדירוג הספציפיות

מהי הספציפיות של a[href=/learn/css/%22#%22%5D%3C%2Fcode>?

(0,0,1)
הערך של a הוא (0,0,1), אבל הערך של [href=/learn/css/%22#%22%5D%3C%2Fcode> הוא (0,1,0).
(0,1,0)
כדאי לנסות שוב. הערך של a הוא (0,0,1), אבל הערך של [href=/learn/css/%22#%22%5D%3C%2Fcode> הוא (0,1,0).
(0,1,1)
הערך של a הוא (0,0,1) והערך של [href=/learn/css/%22#%22%5D%3C%2Fcode> הוא (0,1,0), כך שהספציפיות הכוללת היא (0,1,1).

גורמים שלא משפיעים על הספציפיות

יש כמה תפיסות שגויות נפוצות לגבי הגורמים הבאים שמשפיעים על הספציפיות.

מאפייני סגנון בתוך השורה

‫CSS שמוחל ישירות על מאפיין style של רכיב, לא משפיע על הספציפיות כי זה שלב אחר במפל שמוערך לפני הספציפיות.

<div style="color: red"></div>

כדי לבטל את ההצהרה הזו מתוך גיליון סגנונות, צריך להשתמש בשיטה שתגרום להצהרה לנצח בשלב מוקדם יותר של הקסקדה.

לדוגמה, אפשר להוסיף את !important, כך שהוא יהפוך לחלק ממקור !important שיצרת.

!important הצהרות

הוספת !important בסוף הצהרת CSS לא משפיעה על הספציפיות, אבל היא משייכת את ההצהרה למקור אחר, כלומר Authored !important.

בדוגמה הבאה, הספציפיות של .my-class לא רלוונטית כדי שההצהרה !important תנצח.

.my-class {
  color: red !important;
  color: white;
}

אם יש שתי הצהרות !important, הספציפיות נכנסת לתמונה שוב, כי שלב המקור מהמפל לא הצליח לקבוע עדיין את המנצח.

.branding {
  color: blue !important;
}

button {
  color: red !important;
}

ספציפיות בהקשר

כשמשתמשים בבורר מורכב, כל חלק בבורר הזה תורם לערך הספציפיות. דוגמה לקוד HTML:

<a class="my-class another-class" href="#">A link</a>

הקישור הזה כולל שתי כיתות. לכלל הבא ב-CSS יש ספציפיות של (0,0,1):

a {
  color: red;
}

אם אתם מפנים לאחת מהמחלקות בכלי הבחירה, עכשיו יש לה ספציפיות של (0,1,1):

a.my-class {
  color: green;
}

מוסיפים את הכיתה השנייה לבורר, עכשיו יש לה ספציפיות של (0,2,1):

a.my-class.another-class {
  color: rebeccapurple;
}

מוסיפים את המאפיין href לבורר, עכשיו יש לו ספציפיות של (0,3,1):

a.my-class.another-class[href] {
  color: goldenrod;
}

לבסוף,מוסיפים :hover פסאודו-קלאס לכל זה, הספציפיות של הבורר היא (0,4,1):

a.my-class.another-class[href]:hover {
  color: lightgrey;
}

בדיקה של רמת ההבנה שלך

בודקים את הידע בנוגע לדירוג הספציפיות

לאיזה מהסלקטורים הבאים יש ספציפיות של (0,2,1)?

article > section
רכיבים מוסיפים ספציפיות כמו של רכיב (`C` component). יש 2 רכיבים בסלקטור, ולכן הספציפיות שלו היא (0,0,2).
article.card.dark
רכיבי Elements מוסיפים ספציפיות כמו של רכיב (רכיב C), ורכיבי Classes מוסיפים ספציפיות כמו של מחלקה (רכיב B). אם יש 2 מחלקות ואלמנט אחד, הספציפיות של הסלקטור היא (0,2,1).
article:hover a[href]
אלמנטים מוסיפים ספציפיות כמו של אלמנט (`C` component), פסאודו-מחלקות ומאפיינים מוסיפים ספציפיות כמו של מחלקה (`B` component). יש 2 בוררי אלמנטים (2 × (0,0,1)), בורר מאפיינים (בשווי (0,1,0)) ובורר מחלקה (בשווי (0,1,0)). לכן, הספציפיות הכוללת של הבורר הזה היא (0,2,2).

הגדלת הספציפיות באופן פרגמטי

נניח שיש לכם קוד CSS שנראה כך:

.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

עם קוד HTML שנראה כך:

<button class="my-button" onclick="alert('hello')">Click me</button>

הרכיב הזה מופיע עם רקע אפור, כי הסלקטור השני כולל ספציפיות של (0,1,1). הסיבה לכך היא שיש לו בורר סוג אחד (button), שהוא (0,0,1), ובורר מאפיינים אחד ([onclick]), שהוא (0,1,0).

הכלל הקודם – .my-button – שווה ל-(0,1,0) כי יש לו סלקטור מחלקה אחד, שהוא ספציפיות נמוכה יותר מ-(0,1,1).

אם רוצים להגדיל את ההשפעה של הכלל הזה, אפשר לחזור על בורר המחלקה כך:

.my-button.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

עכשיו, הרקע של הכפתור יהיה כחול, כי הסלקטור החדש מקבל ספציפיות (0,2,0)

אם יש שוויון במידת הספציפיות, המערכת עוברת לשלב הבא בשרשרת.

נמשיך עם הדוגמה של הלחצן, ונשנה את ה-CSS כך:

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

לכפתור יש רקע אפור, כי לשני הסלקטורים יש ספציפיות זהה של (0,1,0).

אם מחליפים את הכללים בסדר המקור, הכפתור יהיה כחול.

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

הסיבה לכך היא שלשני הסלקטורים יש את אותה רמת ספציפיות. במקרה כזה, המערכת תחזור לשלב הסדר שבו המודעות מופיעות.

משאבים