CSS พื้นฐาน + Selectors + Specificity
3 วิธีใส่ CSS, Selector 6 แบบที่ใช้จริง และเคลียร์ปัญหาคลาสสิก "เขียน CSS แล้วไม่ยอมเปลี่ยน" ด้วย specificity 4-tuple พร้อม !important ที่ใช้ให้ถูก
บทที่แล้วเราเรียนกันไปว่า HTML คือโครงสร้างที่ browser แปลงเป็นต้นไม้ แต่ต้นไม้เปล่าๆ browser จะแสดงด้วย default ที่น่าเกลียดมาก ตัวอักษรสีดำ ปุ่มสีเทา ไม่มี spacing อะไรทั้งสิ้น
CSS คือภาษาที่บอก browser ว่า "element แต่ละตัวของ HTML จะหน้าตาเป็นยังไง" สี ตัวอักษร ขนาด ระยะห่าง ทุกอย่างที่ทำให้เว็บดูไม่เหมือน document Word คือผลของ CSS ทั้งหมด
บทนี้จะตอบ 3 คำถามที่มือใหม่ทุกคนเจอ:
- CSS แปะไว้ตรงไหนได้บ้าง (มี 3 วิธี แต่ละวิธีใช้ตอนไหน)
- Selector คืออะไร browser รู้ได้ยังไงว่า style ไหนจะไปใช้กับ element ไหน
- เขียน CSS แล้วไม่ยอมเปลี่ยน ทำไม? ปัญหาที่ทุกคนเจอ คำตอบคือเรื่องของ specificity
3 วิธีใส่ CSS
ก่อนไปเรื่อง selector ต้องตอบก่อนว่า CSS แปะไว้ตรงไหนได้บ้าง มีทั้งหมด 3 วิธี ลองกดดูแต่ละแบบเปรียบเทียบกัน
style="..." ติดกับ tag เลย
<h1 style="color: #7c3aed; font-size: 28px;">
ขอต้อนรับ
</h1>
<p style="color: #475569; line-height: 1.6;">
เขียน style ติดกับ tag ข้อดีคือเห็นเลย
แต่ใช้ซ้ำไม่ได้ ดูแลยาก
</p>ทั้ง 3 วิธีใช้ได้หมด แต่ external file คือมาตรฐานในงานจริง ด้วยเหตุผลต่อไปนี้:
- แก้ที่เดียวมีผลทุกหน้า เปลี่ยนสี brand ครั้งเดียว ไม่ต้องตามแก้ 20 ไฟล์
- browser สามารถ cache ไฟล์ .css ได้ ตอนเข้าหน้าที่ 2 ที่ 3 จะโหลดเร็วกว่า
- แยก HTML กับ CSS ออกจากกัน อ่านง่ายกว่ารวมทุกอย่างไว้ในไฟล์เดียว
Selector คือภาษา query
เวลาเขียน CSS จะเจอรูปแบบนี้เสมอ:
selector {
property: value;
}Selector คือ "ใครจะได้ style นี้" browser ต้องรู้ก่อนว่า style ที่เขียนจะไปใช้กับ element ไหน ไม่งั้นจะใส่ให้ทั้งหน้าเว็บก็ไม่ใช่
ลองคิดว่า selector เหมือนภาษา query ที่บอก browser ว่า "หา element ที่เข้าเงื่อนไขนี้" แล้ว browser ก็ไปเลือกให้
Selector 3 แบบที่เจอถึง 80% ในงานจริง:
pคือ Tag selector เลือก element ตามชื่อ tag.btnคือ Class selector ใช้จุดนำหน้าชื่อ class ตัวที่ใช้บ่อยที่สุดในงานจริง#submitคือ Id selector ใช้ # นำหน้าชื่อ id สำหรับ element ที่มีอยู่ตัวเดียวต่อหน้า
ตัวเสริม (ใช้น้อยกว่าแต่ต้องรู้):
nav aคือ Descendant เว้น space หมายถึง "ลูกหลาน" กี่ชั้นก็ได้nav > aคือ Direct child ใช้ > หมายถึง "ลูกตรง" ชั้นเดียวเท่านั้นa:hoverคือ Pseudo-class ใช้ : หมายถึง "ตอนอยู่ในสภาวะนี้" เช่น hover, focus, active, disabled.btn.primaryคือ Multiple class ติดกันหมายถึง AND ต้องมีทั้ง 2 class อยู่บน element เดียวกัน
เขียน CSS แล้วไม่ยอมเปลี่ยน คำตอบคือ Specificity
นี่คือช่วงที่มือใหม่ทุกคนเจอครั้งแรกแล้วงง เขียน .card { color: red; } แล้ว element ไม่ยอมเป็นสีแดง ทั้งที่ selector ถูก import CSS ถูก save แล้ว refresh แล้วก็ยังไม่ได้
คำตอบคือ มี CSS rule อื่น "เสียงดังกว่า" ทับอยู่ browser จะฟังอันที่ specificity สูงกว่า
Specificity เป็นคะแนนที่คิดจาก 4 ส่วน เขียนเป็น (inline, id, class, tag):
p {...}คือ (0, 0, 0, 1) ใช้ tag 1 ตัว.btn {...}คือ (0, 0, 1, 0) ใช้ class 1 ตัว.btn.primary {...}คือ (0, 0, 2, 0) ใช้ class 2 ตัว#submit {...}คือ (0, 1, 0, 0) ใช้ id 1 ตัวstyle="..."คือ (1, 0, 0, 0) inline สูงสุด
การเปรียบเทียบคือ เทียบทีละตำแหน่งจากซ้ายไปขวา เจอตัวไหนต่างกันก่อน ฝั่งมากกว่าชนะทันที ไม่ต้องดูตัวถัดไป
นี่คือเหตุผลที่ #submit = (0,1,0,0) ชนะ .btn.primary = (0,0,2,0) ทั้งที่ดูเหมือน class 2 ตัวน่าจะเยอะกว่า id 1 ตัว เพราะเทียบที่ตำแหน่ง id ก่อน 1 มากกว่า 0 จบเลย ไม่ดู class count
ลองเล่นดู เปิดปิด rule และลองเปิด !important:
ทั้ง 4 rule แย่งกันเปลี่ยนสีปุ่มเดียวกัน กดปุ่ม !important เพื่อดูว่าเปลี่ยนผลลัพธ์ยังไง
buttontag เดียว น้ำหนักน้อยสุด
.btn1 class น้ำหนักมากกว่า tag
.btn.primary2 class น้ำหนักมากกว่า class เดี่ยว
#buy1 id ชนะ class กี่อันก็ได้
- เทียบ 4 ตัวจาก ซ้ายไปขวา: inline, id, class, tag
- ตัวแรกที่ต่างกัน ฝั่งมากกว่าชนะ ทันที (ไม่ดูตัวถัดไป)
#buy= (0,1,0,0) ชนะ.btn.primary= (0,0,2,0) เพราะ id > class- เท่ากัน? อันที่เขียนทีหลังใน CSS ชนะ (cascade)
!important ทำไมพี่ในทีมถึงห้าม
!important คือ "ฉันไม่สนจะเกิดอะไร ให้ rule นี้ชนะเสมอ" เขียนต่อท้าย value:
.btn {
color: blue !important;
}ใช้ได้เหมือนกัน แต่ปัญหาคือ พอใส่ไปแล้ว ครั้งถัดไปที่อยาก override rule นี้ ก็ต้องใช้ !important อีก กลายเป็นสงครามที่แต่ละฝ่ายแข่งกันดัง สุดท้าย CSS รกจนไม่มีใครอยากแตะ
สรุป
- CSS แปะได้ 3 วิธีคือ inline,
<style>tag, และ external file งานจริงใช้ external เป็นหลัก - Selector คือตัวบอกว่า "ใครได้ style นี้" มี 3 แบบหลักคือ tag,
.class,#id - Class ใช้บ่อยที่สุดในงานจริง เพราะ reuse ง่าย ไม่ผูกกับ HTML structure ตายตัว
- Specificity ตัดสินว่า rule ไหนชนะเวลาขัดกัน คิดเป็น 4-tuple (inline, id, class, tag) เทียบจากซ้ายไปขวา
- เขียน CSS แล้วไม่เปลี่ยน 90% เป็นเพราะ specificity ของ rule อื่นสูงกว่า ไม่ใช่ code ผิด
!importantชนะเสมอ แต่เก็บไว้ใช้เฉพาะกรณีสุดทางเท่านั้น
บทถัดไปคือ Box Model เราจะได้เข้าใจว่าทำไม width: 100% แล้ว element ถึงล้นกรอบ ทำไมใส่ padding เข้าไปแล้ว element ใหญ่ขึ้น ทุก element ใน HTML คือกล่องที่มี 4 ชั้น เข้าใจเรื่องนี้แล้ว layout อะไรก็ไม่งงอีกต่อไป