ลักษณะเฉพาะ

พอดแคสต์ CSS - 003: ความเฉพาะเจาะจง

สมมติว่าคุณกำลังทำงานกับ HTML และ CSS ต่อไปนี้

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

button {
  color: red;
}

ในที่นี้มีกฎ 2 ข้อที่กำหนดเป้าหมายเป็นองค์ประกอบเดียวกัน กฎแต่ละข้อมีการประกาศที่ต้องการกำหนดสีของปุ่ม โดยกฎหนึ่งพยายามกำหนดสีปุ่มเป็นสีแดง และอีกกฎหนึ่งพยายามกำหนดสีปุ่มเป็นสีน้ำเงิน การประกาศใดที่จะนำไปใช้กับองค์ประกอบ

การทำความเข้าใจอัลกอริทึมความเฉพาะเจาะจงของ CSS เป็นกุญแจสำคัญในการทำความเข้าใจวิธีที่ CSS ตัดสินใจเลือกการประกาศที่แข่งขันกัน

ความเฉพาะเจาะจงเป็นหนึ่งในขั้นตอนที่แตกต่างกันของ Cascade ซึ่งเราได้กล่าวถึงในโมดูลที่แล้วในหัวข้อ Cascade

การให้คะแนนความเฉพาะเจาะจง

กฎตัวเลือกแต่ละข้อภายใน ต้นทาง จะได้รับการให้คะแนน คุณสามารถคิดว่าความเฉพาะเจาะจงเป็นคะแนนรวม และตัวเลือกแต่ละประเภทจะได้รับคะแนนสำหรับคะแนนรวมนั้น การประกาศจากกฎที่มีความเฉพาะเจาะจงสูงสุดจะเป็นผู้ชนะ

ความเฉพาะเจาะจงในโปรเจ็กต์จริงคือการสร้างความสมดุลเพื่อให้แน่ใจว่ากฎ CSS ที่คุณต้องการนำไปใช้ จะถูกนำไปใช้จริง ในขณะเดียวกันก็พยายามรักษาระดับคะแนนให้ต่ำเพื่อป้องกันความซับซ้อน ความเฉพาะเจาะจงควรสูงเท่าที่เราต้องการเท่านั้น ไม่ใช่พยายามให้มีความเฉพาะเจาะจงสูงสุดเท่าที่จะเป็นไปได้ เนื่องจากในอนาคตอาจต้องนำ CSS ที่สำคัญกว่ามาใช้ หากคุณเลือกความเฉพาะเจาะจงสูงสุด คุณจะทำให้งานนั้นยากขึ้น

ความเฉพาะเจาะจงไม่ใช่เลขทศนิยม แต่เป็นสามสิ่งประกอบด้วย 3 องค์ประกอบ ได้แก่ A, B และ C

  • A: ความเฉพาะเจาะจงแบบรหัส
  • B: ความเฉพาะเจาะจงแบบคลาส
  • C: ความเฉพาะเจาะจงแบบองค์ประกอบ

โดยมักแสดงด้วยสัญกรณ์ (A,B,C) เช่น (1,0,2) นอกจากนี้ยังมีการใช้สัญกรณ์ A-B-C เป็นทางเลือกแทนกันโดยทั่วไป

แผนภาพแสดงองค์ประกอบ 3 อย่างของความจำเพาะ (A,B,C) สำหรับแต่ละคอมโพเนนต์ ไดอะแกรมจะแสดงสิ่งที่คอมโพเนนต์นั้นแสดงและตัวอย่างตัวเลือกบางอย่างที่มีผลต่อคอมโพเนนต์
แผนภาพแสดงองค์ประกอบความเฉพาะเจาะจงที่ตัวเลือกต่างๆ ส่งผล

วิธีเปรียบเทียบความเฉพาะเจาะจง

ความเฉพาะเจาะจงจะเปรียบเทียบโดยการเปรียบเทียบองค์ประกอบทั้ง 3 ตามลำดับ ความเฉพาะเจาะจงที่มีค่า A มากกว่าจะมีความเฉพาะเจาะจงมากกว่า หากค่า A ทั้ง 2 ค่าเท่ากัน ความเฉพาะเจาะจงที่มีค่า B มากกว่าจะมีความเฉพาะเจาะจงมากกว่า หากค่า B ทั้ง 2 ค่าเท่ากัน ความเฉพาะเจาะจงที่มีค่า C มากกว่าจะมีความเฉพาะเจาะจงมากกว่า หากค่าทั้งหมดเท่ากัน ความเฉพาะเจาะจงทั้ง 2 จะเท่ากัน

ตัวอย่างเช่น (1,0,0) ถือว่ามีความเฉพาะเจาะจงสูงกว่า (0,4,3) เนื่องจากค่า A ใน (1,0,0) (ซึ่งคือ 1) มากกว่าค่า A จาก (0,4,3) (ซึ่งคือ 0)

ตัวเลือกส่งผลต่อความเฉพาะเจาะจง

แต่ละส่วนในสามสิ่งประกอบของความเฉพาะเจาะจงเริ่มต้นด้วยค่า 0, ดังนั้นความเฉพาะเจาะจงเริ่มต้นคือ (0,0,0). แต่ละส่วนของตัวเลือกจะเพิ่มความเฉพาะเจาะจง ซึ่งจะเพิ่มค่าของ A, B หรือ C ขึ้นอยู่กับประเภทของตัวเลือก

Universal selector

ตัวเลือกส่วนกลาง universal selector (*) ไม่เพิ่มความเฉพาะเจาะจง โดยจะคงค่าไว้ที่ความเฉพาะเจาะจงเริ่มต้น (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;
}

ตัวเลือกรหัส

ตัวเลือกรหัส จะเพิ่มความเฉพาะเจาะจงแบบรหัส ซึ่งจะเพิ่มองค์ประกอบ 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 ขององค์ประกอบ โดยตรงจะไม่มีผลต่อความเฉพาะเจาะจง เนื่องจากเป็นขั้นตอนอื่นใน Cascade ที่มีการประเมินก่อนความเฉพาะเจาะจง

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

หากต้องการลบล้างการประกาศนี้จากภายในสไตล์ชีต คุณต้องใช้การประกาศที่ชนะในขั้นตอนก่อนหน้าของ Cascade

ตัวอย่างเช่น คุณสามารถเพิ่ม !important ลงในการประกาศนี้ เพื่อให้การประกาศนี้เป็นส่วนหนึ่งของ ต้นทาง !important ที่เขียนขึ้น

การประกาศ !important

!important ที่ส่วนท้ายของการประกาศ CSS จะไม่มีผลต่อความเฉพาะเจาะจง แต่จะวางการประกาศไว้ในต้นทางอื่น นั่นคือ ต้นทาง !important ที่เขียนขึ้น

ในตัวอย่างต่อไปนี้ ความเฉพาะเจาะจงของ .my-class ไม่เกี่ยวข้องกับการประกาศ !important ที่จะชนะ

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

เมื่อการประกาศ 2 รายการเป็น !important ความเฉพาะเจาะจงจะมีบทบาทอีกครั้ง เนื่องจากขั้นตอนต้นทางจาก Cascade ยังไม่สามารถกำหนดผู้ชนะได้

.branding {
  color: blue !important;
}

button {
  color: red !important;
}

ความเฉพาะเจาะจงตามบริบท

เมื่อใช้ตัวเลือกที่ซับซ้อนหรือตัวเลือกผสม แต่ละส่วนของตัวเลือกนั้นจะรวมกันเป็นความเฉพาะเจาะจง ดูตัวอย่าง HTML นี้

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

ลิงก์นี้มี 2 คลาส กฎใน 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 คลาสและ 1 องค์ประกอบ ตัวเลือกนี้จะมีความเฉพาะเจาะจง (0,2,1)
article:hover a[href]
องค์ประกอบจะเพิ่มความเฉพาะเจาะจงแบบองค์ประกอบ (องค์ประกอบ `C`) คลาสเสมือนและแอตทริบิวต์จะเพิ่มความเฉพาะเจาะจงแบบคลาส (องค์ประกอบ `B`) มีตัวเลือกองค์ประกอบ 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>

ปุ่มมีพื้นหลังสีเทา, เนื่องจากตัวเลือกที่ 2 มี ความเฉพาะเจาะจง (0,1,1) เนื่องจากมีตัวเลือกประเภท 1 รายการ (button) ซึ่งมีค่า (0,0,1) และตัวเลือกแอตทริบิวต์ ([onclick]) ซึ่งมีค่า (0,1,0)

กฎก่อนหน้าอย่าง .my-button มีค่าเท่ากับ (0,1,0) เนื่องจากมีตัวเลือกคลาส 1 รายการ ซึ่งมีความเฉพาะเจาะจงต่ำกว่า (0,1,1)

หากต้องการเพิ่มความเฉพาะเจาะจงให้กับกฎนี้ คุณสามารถทำซ้ำตัวเลือกคลาสได้ดังนี้

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

button[onclick] {
  background: grey;
}

ตอนนี้ปุ่มจะมีพื้นหลังสีน้ำเงิน เนื่องจากตัวเลือกใหม่มีความเฉพาะเจาะจง (0,2,0)

หากความเฉพาะเจาะจงเท่ากัน ระบบจะย้อนกลับไปใช้ขั้นตอนถัดไปใน Cascade

กลับมาดูตัวอย่างปุ่มกันก่อน แล้วเปลี่ยน CSS เป็นดังนี้

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

ปุ่มมีพื้นหลังสีเทา เนื่องจากตัวเลือกทั้ง 2 มีความเฉพาะเจาะจงเหมือนกัน คือ (0,1,0)

หากคุณเปลี่ยนกฎตามลำดับที่ปรากฏในแหล่งที่มา ปุ่มจะเป็นสีน้ำเงิน

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

เนื่องจากตัวเลือกทั้ง 2 มีความเฉพาะเจาะจงเหมือนกัน ในกรณีนี้ Cascade จะย้อนกลับไปใช้ขั้นตอนลำดับที่ปรากฏ

แหล่งข้อมูล