ספציפיות

The CSS Podcast – 003: Specificity

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

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

button {
  color: red;
}

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

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

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

ציון ספציפיות

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

כשמדובר בפרויקט ספציפי, צריך לשמור על איזון: לוודא שכללי ה-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;
}

בורר של סיווג, פסאודו-סיווג או מאפיין

בורר של סוג, פסאודו-סוג או מאפיין מוסיף ספציפיות שדומה לסוג, שמגדילה את המרכיב B ב-1.

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

בורר הכיתות

.my-class {
  color: red;
}

סלקטור של פסאודו-כיתה

:hover {
  color: red;
}

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

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

בורר מזהה

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

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

#myID {
  color: red;
}

בוררים אחרים

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

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

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

לדוגמה הזו יש ספציפיות של (0,1,1) כי יש לה בורר סוג אחד (div) וכיתה אחת בתוך ה-:not().

בדיקת ההבנה

בדיקת הידע שלכם בנושא ניקוד ספציפיות

מה מידת הספציפיות של a[href="#"]?

(0,0,1)
הערך של a הוא (0,0,1), אבל הערך של [href="#"] הוא (0,1,0).
(0,1,0)
כדאי לנסות שוב. הערך של a הוא (0,0,1), אבל הערך של [href="#"] הוא (0,1,0).
(0,1,1)
הערך של a הוא (0,0,1) והערך של [href="#"] הוא (0,1,0), כך שהספציפיות הכוללת היא (0,1,1).

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

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

מאפייני סגנון מוטבעים

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

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

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

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

!important הצהרות

הערך !important בסוף הצהרת CSS לא משפיע על הספציפיות, אבל הוא מעביר את ההצהרה למקור אחר, כלומר !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'). יש 2 רכיבים בבורר, ולכן רמת הספציפיות שלו היא (0,0,2).
article.card.dark
רכיבים מוסיפים ספציפיות של רכיבים (רכיב 'C'), וכיתות מוסיפות ספציפיות של כיתות (רכיב 'B'). עם 2 כיתות ורכיב אחד, הספציפיות של הבורר הזה היא (0,2,1).
article:hover a[href]
אלמנטים מוסיפים ספציפיות שדומה לאלמנט (רכיב 'C'), פסאודו-כיתות ומאפיינים מוסיפים ספציפיות שדומה לכיתה (רכיב 'B'). יש 2 בוררי רכיבים (2 x (0,0,1)), בורר מאפיין (ערך (0,0,1)) ובורר רמה (ערך (0,0,1)). לכן, רמת הספציפיות הכוללת של הבורר הזה היא (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;
}

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

משאבים