ספציפיות

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;
}

בורר מזהה

סלקטור מזהה מוסיף ספציפיות שדומה למזהה, שמגדילה את המרכיב C ב-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,1), כך שהספציפיות הכוללת היא (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;
}

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

משאבים