css specificity

CSS Specificity พื้นฐานการเขียน CSS ที่คนส่วนใหญ่ไม่รู้ & เทคนิค Hack

เวลาเราเขียน CSS ในโปรเจคเว็บไซต์ใหญ่ ๆ ที่มี CSS Selector มากมาย ปัญหาที่น่าจะเจอกันบ่อย ๆ คือ CSS เขียนแล้วไม่ยอมทำงาน เพราะโดน Selector ตัวอื่นเขียนทับ ต้องไปใส่ !important เพื่อบังคับให้มันทำงาน

การใส่ !important ช่วยแก้ปัญหาได้รวดเร็วก็จริง แต่ยิ่งเราเขียนโค้ดเยอะ ๆ ก็จะพบว่าการใส่ !important จะทำให้มีปัญหาตีกับ Element อื่นตลอดเวลา และทำให้โค้ดดูแลยากในระยะยาวด้วยครับ ยิ่งกับโปรเจคใหญ่ ๆ เรายิ่งไม่ควรใช้ !important เลย

บทความแนะนำ: CSS Guideline – เทคนิคการเขียน CSS แบบมืออาชีพ

CSS Specificity คืออะไร? รู้แล้วจะเลิกใช้ !important

CSS Specificity เป็นคอนเซปต์การเลือก CSS Selector ที่มี “ค่าพลัง” เยอะที่สุด มาใช้กับ Element บนหน้าเว็บไซต์ครับ (ขอใช้คำนี้แล้วกันครับ มันดูเป็นการ์ดพลังต่อสู้อะไรแบบนั้นดี ใครพลังเยอะกว่าก็ชนะ 55)

ภาษา CSS เหมือนภาษาการเขียนโปรแกรมอื่น ๆ ที่อ่านโค้ดจาก บน – ล่าง ครับ แปลว่า ถ้าเขียน CSS Selector ซ้ำกัน 2 อัน อันล่างจะถูกนำไปใช้งานเพราะใหม่กว่า แต่ความพิเศษของ CSS คือ Selector ที่แตกต่างกันแต่ละ Selector จะมี “ค่าพลัง” ไม่เหมือนกัน และเมื่อไหร่ที่ “ค่าพลัง” ต่างกัน ลำดับการเขียนโค้ดจะไม่มีผลทันที

ตัวอย่างง่าย ๆ ของเรื่อง CSS Specificity คือ ถ้าเราเขียน HTML/CSS แบบนี้:

HTML:

<div class="box" id="redbox">

CSS:

.box { background: white; } /* ให้ .box พื้นหลังสีขาว */
#redbox { background: red; } /* ให้ #redbox พื้นหลังสีแดง */

เราจะได้กล่องสีแดงมา 1 กล่อง ดังด้านล่างนี้ครับ

1723

CSS Selector ทั้ง 2 อันของเรามีค่าพลังต่างกัน โดยค่าพลังจะคำนวณจากการใช้ class, ID, !important หรือการซ้อน Element หลาย ๆ ชั้นครับ ยิ่ง Selector นั้นเจาะจงเท่าไหร่ยิ่งมีค่าพลังเยอะเท่านั้น เรามาดูกันว่า CSS Selector ในตัวอย่างมีค่าพลังเท่าไหร่กันบ้าง

.box { } มีค่าพลัง 10

#redbox { } มีค่าพลัง 100

เพราะฉะนั้น CSS Property ใน #redbox { } (ค่าพลัง 100) จะถูกดึงมาแสดงผลทับของ .box { } (ค่าพลัง 10) เพราะมี ค่าพลังมากกว่า นั่นเอง

ลองมาดูอีกตัวอย่างกันนะครับ

HTML:

<div class="box">
<a href="#">Hello</a>
</div>

CSS:

a { color: blue; } /* ให้ <a> ตัวหนังสือสีน้ำเงิน */
.box a { color: red; } /* ให้ <a> ที่อยู่ใน .box ตัวหนังสือสีแดง */

ถึงแม้เราจะกำหนดให้ <a> เป็นสีเงินในบรรทัดแรก ลิงค์ก็จะกลายเป็นสีแดงเพราะไปเจอ .box a { } ที่มีค่าพลังเยอะกว่าครับผม

1723

ในตัวอย่างนี้ a { } มีค่าพลัง 1 ในขณะที่ .box a { } มีค่าพลัง 11 ครับ ทำให้บราวเซอร์เลือก CSS Selector อันหลังมาแสดงแทนนั่นเอง

ต่อไปเรามาดูกันครับว่า “ค่าพลัง” คำนวณยังไง

วิธีคำนวณ “ค่าพลัง” CSS Specificity

เราสามารถคำนวณตามกฏง่าย ๆ แบบนี้เลยครับ โดยทุก Selector เริ่มจาก 0 คะแนน:

  1. ถ้าเขียน CSS ใส่ใน style เลย (เช่น <a style="color: red">) : +1000 คะแนน
  2. ใส่ 1 ID : +100 คะแนน
  3. ใส่ 1 class : +10 คะแนน
  4. ใส่ 1 Element (แท็ก HTML ทั้งหลาย) :  +1 คะแนน

มาลองคำนวณ “ค่าพลัง” กันดูนะคร้าบ

  • div { } = 1 คะแนน (ข้อ 4)
  • .box { } = 10 คะแนน (ข้อ 3)
  • #box { } = 100 คะแนน (ข้อ 2)

ในกรณีที่ CSS Selector เราซ้อนหลายชั้น ก็คิดคะแนนโดย + ทุกชั้นรวมกันไปเลยครับ เช่น

  • div a { } = 2 คะแนน เพราะ div และ a คิดเป็น Element ละ 1 คะแนน (ข้อ 4)
  • .box a { } = 11 คะแนน เพราะ .box = 10 คะแนน (ข้อ 3) และ a = 1 คะแนน (ข้อ 4)
  • div h1 a span { } = 4 คะแนน เพราะ Element ละ 1 คะแนน (ข้อ 4)
  • .page h1 a { } = 12 คะแนน เพราะ .page = 10 คะแนน (ข้อ 3) และ h1 กับ a = 1 + 1 คะแนน (ข้อ 4)

CSS Selector ตัวไหนมีคะแนนเยอะกว่าก็จะถูกนำมาใช้งาน ซึ่งในกรณีที่คะแนนเท่ากัน ก็มาดูว่า CSS Selector ตัวไหนอยู่ด้านล่างครับ ตัวด้านล่างจะถูกนำมาใช้งาน

ซึ่งถ้าสังเกตในข้อ 1 จะเห็นว่าถ้าเราเขียน Inline CSS (เขียน CSS ลงใน style) จะมีค่าพลังสูงกว่าทุกอย่างครับ (1000 คะแนน) เพราะฉะนั้นเวลาเราอยากบังคับใช้ CSS อะไรสามารถเขียนเป็น Inline CSS ได้ แต่แนะนำให้ใช้เวลาทดสอบเฉย ๆ ครับ เว็บไซต์จริงไม่ควรมี CSS ใน HTML เพราะจะทำให้ไฟล์ HTML หนัก โหลดช้า เป็นผลเสียต่อ SEO ครับ

สำหรับ !important ที่ไม่ได้อยู่ในข้อ 1 – 4 ด้านบน ถ้าใส่เข้าไปจะเป็นการบังคับใช้ CSS นั้นทันที ไม่ต้องดู “ค่าพลัง” กันเลยครับ (หรือจะใช้วิธีจำว่าค่าพลัง Infinity เลยก็ได้ครับ)

เทคนิคการเขียนให้ CSS ของเราไม่ตีกับ CSS Specificity  โดย CSSWizardry

ถ้าเราต้องมานั่งคิดคะแนนทุกครั้งที่เขียน Selector คงยุ่งยากน่าดูครับ เพราะฉะนั้นมาดูเทคนิคง่าย ๆ ที่จะทำให้โค้ด CSS ของเราจัดการได้สะดวก ซึ่งแนะนำโดยคุณ Harry เจ้าของเว็บไซต์ CSSWizardry.com ซึ่งเป็น Front-end Developer ที่เก่งมาก ๆ

  • อย่าใช้ #ID ใน Selector – การใช้ ID ซึ่งเพิ่มค่าพลังถึง 100 คะแนน จะทำให้ความแตกต่างระหว่าง Selector มากเกินไป และเขียนทับได้ยากครับ (ถ้าจะทับต้องเขียน ID, Inline CSS, หรือ !important เท่านั้น ซึ่งทำให้โค้ดหนักเข้าไปใหญ่)
  • อย่าเขียน Selector ซ้อนกันโดยไม่จำเป็น – เขียน .box-inside { } จะดีกว่าเขียน .box .box-inside { } ซึ่งเป็นการซ้อนโดยไม่จำเป็น และทำให้ค่าพลังสูงกว่าเดิมโดยไม่จำเป็น ยกเว้นว่าแบบแรกไม่ทำงาน
  • อย่าเขียน Selector ชั้นเดียวแบบเจาะจงเกินไป – เขียน .box { } จะดีกว่าเขียน div.box { } ซึ่งเป็นการเจาะจงเกินไป ทำให้ค่าพลังสูงกว่าเดิมโดยไม่จำเป็น ยกเว้นว่าแบบแรกไม่ทำงาน
  • ใช้ Class ให้บ่อยเข้าไว้ – Class เป็นการเขียน Selector ที่ค่าพลังไม่สูงเกินไป จะเขียนทับก็ไม่ยาก หรือจะสามารถนำมาใช้ใหม่ก็ทำได้ง่าย

สรุปสั้น ๆ คือ อย่าเขียน CSS Selector ที่เจาะจงเกินความจำเป็นครับ

บางคนอ่านแล้วอาจจะสงสัยว่า ถ้าทำตามเทคนิคนี้แล้วจะเขียน Selector ให้ ID ยังไง หรือจะเพิ่มค่าพลังของ CSS Selector บางตัวได้ยังไงโดยไม่ต้องซ้อนหลายชั้น และไม่ต้องใช้ !important เข้ามาช่วย เรามาดูกันครับ

เทคนิค Hack 1 : เขียน CSS Selector สำหรับ ID แบบค่าพลังน้อย

การใช้ ID ไม่ได้แย่นะครับ เพราะใน HTML มันมีประโยชน์ (เช่น ทำ Hash ให้ลิงค์มาที่จุดนั้น ๆ ได้) แต่ใน CSS นั้น การเขียน Selector ของ ID จะไม่ดี เพราะ “ค่าพลัง” มันเยอะเกินไป ทำให้เราเขียนทับได้ยากครับ

ปกติเราจะเขียน Selector ของ ID แบบนี้:

#box {

}

แบบนี้มี ค่าพลัง = 100 คะแนน ซึ่งถ้าเราเปลี่ยนเป็นแบบนี้:

[id="box] {

}

จะมี ค่าพลัง = 10 คะแนน เท่านั้น เท่ากับการใช้ class selector เลยครับ การเขียนแบบหลังเป็นการใช้ Attribute Selector โดยสามารถเลือก ID ได้เหมือนกับแบบแรกครับ

อย่างไรก็ตาม อย่าลืมว่าวิธีนี้เป็น CSS Hack ให้นำมาใช้เมื่อจำเป็นต้องใช้ ID ในเว็บไซต์จริง ๆ ครับ ถ้าหลีกเลี่ยงได้ก็ใช้ class ทั้งหมดจะดีที่สุดครับผม

เทคนิค Hack 2 : เพิ่มค่าพลัง โดยไม่ต้องซ้อนหลายชั้น

บางครั้งเวลาเขียน CSS จะเจอปัญหา Selector ชั้นเดียวค่าพลังน้อยกว่า Selector หลายชั้น ซึ่งถ้าจะแปลง Selector ชั้นเดียวกลายเป็นหลายชั้นเพื่อให้ค่าพลังมากกว่า เราก็อาจจะต้องไปแก้ไข HTML เพิ่ม class, element เข้าไป มาดูกันว่าจะแก้ปัญหานี้ได้ยังไงโดยไม่ต้องแก้ HTML ครับ

ในสถานการณ์ต่อไปนี้

HTML:

<div class="page">
<div class="button">
<a href="#">Click</a>
</div><!-- button -->
</div><!-- page -->

CSS:

.button { color: red; } /* ค่าพลัง = 10 */
.page a { color: blue; } /* ค่าพลัง = 11 */

จะเห็นว่า .button ไม่กลายเป็นสีแดง เพราะโดน .page a { } ที่มีค่าพลังมากกว่าเขียนทับ ในกรณีนี้ เราสามารถทำแบบนี้ได้ครับ

.button.button { color: red; } /* ค่าพลัง = 20 */
.page a { color: blue; } /* ค่าพลัง = 11 */

การเพิ่ม .button เข้าไปต่อท้าย จะทำให้ “ค่าพลัง” เพิ่มขึ้นเป็น 10 + 10 และทำให้ไม่โดน .page a เขียนทับครับ และจะทำให้ .button ของเรากลายเป็นสีแดงตามที่เราต้องการ

เครื่องมือคำนวณ “ค่าพลัง” CSS Specificity

คิดค่าพลัง CSS Selector

เว็บไซต์ Specificity Calculator เป็นเว็บไซต์ที่คำนวณ “ค่าพลัง” ของ Selector ของเราแบบอัตโนมัติ หากสงสัยว่า Selector ไหนโดนเขียนทับเพราะอะไร ลองเอามาวัดค่าพลังกันในเว็บไซต์นี้ได้เลยครับ

นอกจากนั้นผมคิดว่าเว็บไซต์นี้เหมาะมากเวลาต้องการเรียนรู้เรื่อง CSS Specificity ครับ อันไหนไม่ชัวร์ลองพิมพ์เข้าไปจะรู้ผลทันที

สรุปเรื่อง CSS Specificity เราควรรู้ขนาดไหน

บางคนอาจจะคิดว่าการต้องมาคำนวณ “ค่าพลัง” ของ Selector มันลำบาก (ผมก็คิดเหมือนกันครับ) เพราะฉะนั้นแนะนำให้ขึ้นไปอ่านหัวข้อ “เทคนิคการเขียนให้ CSS ของเราไม่ตีกับ CSS Specificity” ก็ครอบคลุมในระดับที่โอเคแล้วครับ ถ้าเจอปัญหาจริง ๆ ค่อยกลับมาดูเรื่อง CSS Specificity ว่าเราเขียนผิดตรงไหน

สิ่งที่สำคัญมาก ๆ คือ “อย่าใช้ ID ใน CSS ถ้าไม่จำเป็น” หลัง ๆ ผมเขียน CSS โดยใช้ class หมดเลยครับ ซึ่งดีมาก ๆ โดยเฉพาะอย่างยิ่งในเว็บที่ใช้ UI ซ้ำบ่อย ๆ

หากใครยังไม่ได้อ่าน CSS Guideline – เทคนิคการเขียน CSS แบบมืออาชีพ แนะนำให้อ่านต่อเลยครับ อ่านจบจะเขียน CSS เก่งขึ้นเยอะแน่นอน

แวะไปเยี่ยมเยียน สอบถามคำถามต่าง ๆ ได้ที่ Facebook Page ของ Designil นะครับ หากชอบบทความนี้อย่าลืมแชร์ให้เพื่อน ๆ ได้อ่านกัน ความรู้ดี ๆ มีไว้แชร์ครับ 😀







There are no comments

Add yours