جمعبندی و تمرین محاسبه Specificity در انواع Selector
در طول این فصل، با انواع مختلف Selector آشنا شدیم: Type، Class، ID، Universal، Attribute، Combinatorها، Pseudo-class، Pseudo-element و Selectorهای تابعی. حالا وقتشه یه جمعبندی کامل داشته باشیم و ببینیم Specificity هرکدوم از اینها دقیقاً چطور محاسبه میشه و چطور میشه Specificity Selectorهای ترکیبی و پیچیده رو بهدرستی حساب کرد.
مرور چهار سطح Specificity
همونطور که در فصل ۲ دیدیم، Specificity بهصورت یک عدد چهاربخشی نمایش داده میشه: (Inline, ID, Class, Type). حالا بیایم ببینیم هرکدوم از Selectorهایی که در این فصل یاد گرفتیم، دقیقاً تو کدوم سطح قرار میگیرن.
| سطح | Selectorهای این فصل که در این سطح قرار میگیرند |
|---|---|
| ID (سطح ۲) | ID Selector (مثل #header) |
| Class (سطح ۳) | Class Selector، Attribute Selector، Pseudo-class (مثل .box، [type="text"]، :hover، :nth-child()، :checked) |
| Type (سطح ۴) | Type Selector، Pseudo-element (مثل p، ::before، ::first-letter) |
| وزن صفر | Universal Selector (*)، Combinatorها (>، +، ~، فاصله خالی)، :where() |
نکته مهم: خود Combinatorها (فاصله، >، +، ~) هیچ وزنی به Specificity اضافه نمیکنن؛ اونا فقط رابطه بین Selectorها رو مشخص میکنن. وزن نهایی، فقط از جمع Selectorهای واقعی داخل عبارت بهدست میاد.
مثال ۱: ترکیب Combinator با چند Selector
article > h2 + p.intro {
font-size: 18px;
}
بیایم این Selector رو تیکهتیکه بشماریم:
article→ Type Selector → یک واحد Type>→ Combinator → وزن نداردh2→ Type Selector → یک واحد Type+→ Combinator → وزن نداردp→ Type Selector → یک واحد Type.intro→ Class Selector → یک واحد Class
مجموع Specificity: (0, 0, 1, 3) — یک Class و سه Type.
مثال ۲: ترکیب Pseudo-class و Pseudo-element
ul li:nth-child(2)::before {
content: "→ ";
}
ul→ Type → یک واحد Typeli→ Type → یک واحد Type:nth-child(2)→ Pseudo-class → یک واحد Class::before→ Pseudo-element → یک واحد Type
مجموع Specificity: (0, 0, 1, 3) — یک Class و سه Type.
مثال ۳: نقش :is() و :where() در محاسبه
section :is(.card, #featured) p {
color: darkslategray;
}
در اینجا :is() وزن بالاترین آرگومان داخلش رو میگیره. بین .card (Class) و #featured (ID)، بالاترین وزن مربوط به ID است. پس:
section→ یک واحد Type:is(.card, #featured)→ وزن ID را میگیرد → یک واحد IDp→ یک واحد Type
مجموع Specificity: (0, 1, 0, 2).
حالا اگه همین Selector رو با :where() بازنویسی کنیم:
section :where(.card, #featured) p {
color: darkslategray;
}
در این حالت، :where() همیشه وزن صفر داره، صرفنظر از اینکه #featured داخلش باشه. پس Specificity کل فقط از section و p میاد: (0, 0, 0, 2).
مثال ۴: نقش :not() و :has() در محاسبه
li:not(.disabled) {
cursor: pointer;
}
article:has(img.featured) {
border: 2px solid gold;
}
برای li:not(.disabled): li یک واحد Type، و :not() وزن آرگومان داخلش (یعنی .disabled، یک Class) رو میگیره. مجموع: (0, 0, 1, 1).
برای article:has(img.featured): article یک واحد Type، و :has() وزن بالاترین آرگومان داخلش رو میگیره؛ داخلش img (Type) و .featured (Class) هست، پس وزن Class برنده میشه. مجموع: (0, 0, 1, 2).
مثال ۵: یک Selector واقعاً پیچیده
#sidebar .widget-list > li.active:hover a[href^="https"]::after {
color: gold;
}
بیایم قدمبهقدم بشماریم:
#sidebar→ ID → یک واحد ID.widget-list→ Class → یک واحد Class>→ Combinator → وزن نداردli→ Type → یک واحد Type.active→ Class → یک واحد Class:hover→ Pseudo-class → یک واحد Classa→ Type → یک واحد Type[href^="https"]→ Attribute → یک واحد Class::after→ Pseudo-element → یک واحد Type
مجموع Specificity: (0, 1, 4, 3) — یک ID، چهار Class (شامل دو Class واقعی، یک Pseudo-class و یک Attribute)، و سه Type (شامل یک Pseudo-element).
یک اشتباه رایج در محاسبه
خیلیها فکر میکنن طول یا پیچیدگی ظاهری یک Selector، معیار Specificityشه؛ در حالی که فقط نوع Selectorهای داخلش مهمه:
/* این Selector طولانی است، ولی Specificity پایینی دارد */
header nav ul li a {
color: navy;
}
/* این Selector کوتاه است، ولی Specificity بالاتری دارد */
#nav-link {
color: crimson;
}
Selector اول شامل پنج Type Selector است: (0, 0, 0, 5). Selector دوم شامل فقط یک ID است: (0, 1, 0, 0). با وجود اینکه Selector دوم خیلی کوتاهتره، چون سطح ID از سطح Type بالاتره، مقدار #nav-link برنده میشه؛ صرفنظر از تعداد Selectorهای Type در طرف مقابل.
روش عملی برای محاسبه سریع Specificity
برای محاسبه سریع Specificity یک Selector پیچیده، این مراحل رو دنبال کن:
- هر ID Selector رو بشمار و در ستون دوم (ID) قرار بده.
- هر Class، Attribute Selector و Pseudo-class رو بشمار و در ستون سوم (Class) قرار بده.
- هر Type Selector و Pseudo-element رو بشمار و در ستون چهارم (Type) قرار بده.
- Combinatorها، Universal Selector و
:where()رو کاملاً نادیده بگیر (وزن صفر). - برای
:is()،:not()و:has()، فقط وزن بالاترین آرگومان داخلی رو حساب کن، نه مجموع همه آرگومانها.
نکات مهم
- مقایسه Specificity همیشه رقمبهرقم و از چپ به راست انجام میشه، نه با جمع کردن به یک عدد واحد.
- طول ظاهری یک Selector هیچ ارتباطی با میزان Specificityش نداره.
:is()،:not()و:has()وزن بالاترین آرگومان داخلی رو میگیرن؛:where()همیشه وزن صفر داره.
اشتباهات رایج
- تصور اینکه Selectorهای طولانیتر همیشه Specificity بالاتری دارن.
- فراموش کردن اینکه Combinatorها هیچ وزنی اضافه نمیکنن.
- محاسبه اشتباه Specificity برای
:is()،:not()و:has()؛ با جمع کردن همه آرگومانها بهجای گرفتن فقط بالاترین.
جمعبندی
محاسبه Specificity در Selectorهای پیچیده، صرفاً نیازمند شمارش دقیق هر نوع Selector (ID، Class و مشابهها، Type و مشابهها) و نادیده گرفتن Combinatorهاست. برای Selectorهای تابعی مثل :is()، :not() و :has()، فقط وزن بالاترین آرگومان داخلی حساب میشه، در حالی که :where() همیشه وزن صفر دارد. تسلط بر این محاسبه، کلید پیشبینی درست رفتار Cascade در پروژههای واقعیه.
