CSS
Fundamentals
Cascade & Inheritance — กฎไหนชนะ และค่าไหนส่งต่อได้
สองเรื่องที่ผู้เริ่มต้นมักสับสนปนกัน — แคสเคด (Cascade) คือกระบวนการตัดสินว่า rule ไหนชนะเมื่อหลาย rule ชนกัน ส่วนการสืบทอด (Inheritance) คือกลไกที่ทำให้ element ลูกรับค่า property บางอย่างมาจาก element พ่อแม่โดยอัตโนมัติ
Cascade & Inheritance คืออะไร
Cascade ตัดสินว่า rule ไหนชนะ — Inheritance ส่งค่าจาก parent ลงสู่ child อัตโนมัติ
เวลาเขียน CSS คุณอาจสังเกตว่าบาง element "รับ style มาจากที่อื่นโดยอัตโนมัติ" หรือบางครั้ง "เขียน style ไปแล้วแต่ style อื่นกลับชนะ" — ทั้งสองปรากฏการณ์นี้มาจากกลไกคนละตัว แคสเคด (Cascade) คือกระบวนการที่ Browser ใช้ตัดสินว่า เมื่อมีหลาย CSS rule match กับ element เดียวกัน rule ไหนจะมีผลจริง โดยพิจารณาจากความเจาะจง (Specificity) และลำดับการประกาศ การสืบทอด (Inheritance) คือกลไกที่ทำให้ element ลูก (Child) รับค่าของ property บางอย่างมาจาก element พ่อแม่ (Parent) โดยอัตโนมัติ โดยไม่ต้องกำหนดซ้ำ ทั้งสองเรื่องเกี่ยวข้องกัน แต่ตอบคำถามคนละแบบ: — Cascade ตอบว่า "กฎไหนชนะ?" — Inheritance ตอบว่า "ค่าไหนส่งต่อได้?"
ทำไม Cascade & Inheritance ถึงสำคัญ
ทั้งสองเรื่องนี้เป็นรากฐานของการทำงานทุกอย่างใน CSS หากไม่เข้าใจจะพบปัญหาเหล่านี้บ่อยมาก:
- เขียน CSS แล้ว style ไม่ออก — อาจเพราะ rule อื่น cascade มาชนะ
- element ลูกได้ color ที่ไม่คาดหวัง — เพราะรับมาจาก parent ผ่าน Inheritance
- ลบ CSS rule ออกแล้ว element เปลี่ยนสีทันที — เพราะมี rule อื่น cascade มารอแทน
- กำหนด font-family ที่ body แล้วทุก element เปลี่ยนตาม — Inheritance ทำงานอยู่เบื้องหลัง
- สับสนว่าปัญหา style ที่เจอมาจาก "กฎชนกัน" หรือ "ค่าส่งต่ออัตโนมัติ"
- ใช้ !important แก้ทุกอย่างโดยไม่เข้าใจต้นเหตุ
ตัวอย่างจากชีวิตจริง
ลองนึกถึงกฎระเบียบในบริษัทและลักษณะของครอบครัว: 📋 แคสเคด (Cascade) — เหมือนกฎหลายข้อที่บังคับสิ่งเดียวกัน: ผู้จัดการทั่วไปบอก "พนักงานทุกคนใส่เสื้อขาว" หัวหน้าแผนก IT บอก "ทีม IT ใส่เสื้อน้ำเงิน" → พนักงาน IT จะใส่เสื้ออะไร? — ใส่เสื้อน้ำเงิน เพราะกฎของหัวหน้าแผนกเจาะจงกว่า → นั่นคือ Cascade — ตัดสินว่ากฎไหนชนะ 👨👩👧 การสืบทอด (Inheritance) — เหมือนลักษณะที่ส่งต่อในครอบครัว: พ่อตาสีน้ำตาล — ลูกอาจตาสีน้ำตาลตาม แต่ลูกไม่ได้รับ "บ้านของพ่อ" มาโดยอัตโนมัติ (ต้องรับมอบโดยตรง) → บาง property "ส่งต่อได้อัตโนมัติ" เช่น color, font-family → แต่บาง property "ไม่ส่งต่ออัตโนมัติ" เช่น border, margin, padding ลองดูตัวอย่างใน Playground ด้านล่าง — สังเกตว่า p ภายใน div ได้รับ color และ font-family มาจาก parent โดยที่ไม่ได้กำหนดให้ p โดยตรงเลย
แนวคิดหลัก: แคสเคด (Cascade) คืออะไร
แคสเคด (Cascade) คือกระบวนการที่ Browser ใช้ตัดสินว่า เมื่อ CSS rule หลายอัน match กับ element เดียวกันพร้อมกัน rule ไหนจะมีผลจริง Browser ตัดสินโดยพิจารณาปัจจัยตามลำดับ: 1. Importance — rule ที่มี !important ชนะทุกอย่าง (ไม่แนะนำให้ใช้พร่ำเพรื่อ) 2. Specificity (ความจำเพาะ) — selector ที่เจาะจงกว่าชนะ (เช่น #id > .class > element) 3. Source Order (ลำดับการประกาศ) — ถ้า specificity เท่ากัน rule ที่เขียนทีหลังชนะ จำง่าย ๆ ว่า: "เจาะจงกว่าชนะ — ถ้าเท่ากันคนสุดท้ายชนะ"
Cascade ตัดสินตาม Specificity ก่อน — ถ้า Specificity เท่ากันจึงดู Source Order
/* Rule 1: specificity (0,0,0,1) — element selector */
p {
color: gray;
font-size: 16px;
}
/* Rule 2: specificity (0,0,1,0) — class selector — ชนะ Rule 1 */
.message {
color: blue;
}
/* Rule 3: specificity (0,1,0,0) — ID selector — ชนะ Rule 2 */
#important-note {
color: red;
}
/* Rule 4: source order — เขียนทีหลัง Rule 2 แต่ specificity เท่ากัน */
/* ถ้า element มีทั้ง class="message" และ class="alert" จะได้ Rule 4 */
.alert {
color: orange;
}แนวคิดหลัก: การสืบทอด (Inheritance) คืออะไร
property ที่เกี่ยวกับ ตัวอักษร และ สี มักจะ inherit — property ที่เกี่ยวกับ กล่อง และ ระยะห่าง มักจะไม่ inherit
การสืบทอด (Inheritance) คือกลไกที่ทำให้ element ลูก (Child) รับค่าของ property บางอย่างมาจาก element พ่อแม่ (Parent) โดยอัตโนมัติ ไม่ใช่ทุก property จะ inherit — CSS แบ่งไว้ชัดเจนว่า property ไหน "inherit ได้" และ property ไหน "ไม่ inherit":
| ประเภท | Property ตัวอย่าง | Inherit หรือไม่ |
|---|---|---|
| สีและตัวอักษร | color, font-family, font-size, font-weight, line-height | inherit ได้ |
| การจัดข้อความ | text-align, letter-spacing, word-spacing | inherit ได้ |
| การมองเห็น | visibility, cursor | inherit ได้ |
| กล่องและระยะห่าง | margin, padding, width, height | ไม่ inherit |
| กรอบและพื้นหลัง | border, background, box-shadow | ไม่ inherit |
| การจัดวาง | display, position, top, left, flex | ไม่ inherit |
ความต่างระหว่าง Cascade และ Inheritance
ทั้งสองเรื่องมักถูกสับสนกัน เพราะต่างก็เกี่ยวกับ "style ที่ element ได้รับมา" แต่เป็นคนละกลไกกันอย่างสิ้นเชิง:
| หัวข้อ | Cascade (แคสเคด) | Inheritance (การสืบทอด) |
|---|---|---|
| คำถามที่ตอบ | กฎไหนชนะ? | ค่าไหนส่งต่อได้? |
| เกี่ยวกับอะไร | การแข่งขันของ rule ที่ match element เดียวกัน | การรับค่าจาก parent element อัตโนมัติ |
| เกิดขึ้นเมื่อ | มีหลาย rule match กับ element เดียวกัน | element ลูกไม่มี rule กำหนด property นั้นโดยตรง |
| ตัดสินโดย | Specificity และ Source Order | ชนิดของ property (inherit ได้/ไม่ได้) |
| ตัวอย่าง | p { color: gray } vs .note { color: blue } → blue ชนะ | div { color: blue } → p ภายใน div ก็เป็นสีน้ำเงิน |
| ควบคุมด้วย | Specificity ที่สูงขึ้น หรือ !important | กำหนดค่าโดยตรงบน element ลูก หรือใช้ inherit/initial |
การทำงานทีละขั้นตอน: Cascade
- 1. Browser อ่าน HTML และสร้าง DOM (ต้นไม้ element ทั้งหมด)
- 2. Browser อ่าน CSS ทั้งหมด — จาก browser default, external stylesheet, internal style, และ inline style
- 3. สำหรับแต่ละ element — Browser หา CSS rule ทั้งหมดที่ selector match กับ element นั้น
- 4. กรองและจัดเรียง rule ตาม Specificity — rule ที่เจาะจงกว่ามีลำดับสูงกว่า
- 5. ถ้า Specificity เท่ากัน — rule ที่เขียนทีหลัง (Source Order) ชนะ
- 6. ถ้า rule ใดมี !important — rule นั้นขึ้นมาอยู่บนสุดเสมอ
- 7. Browser ใช้ค่าจาก rule ที่ชนะไปแสดงผล element นั้น
การทำงานทีละขั้นตอน: Inheritance
- 1. Browser ต้องแสดงผล element ลูก และต้องการรู้ว่า property X มีค่าอะไร
- 2. ตรวจสอบก่อน: มี CSS rule ใดกำหนด property X ให้กับ element ลูกโดยตรงหรือไม่?
- 3. ถ้ามี — ใช้ค่านั้นเลย (Cascade ตัดสิน) ไม่ต้อง inherit
- 4. ถ้าไม่มี — ตรวจสอบว่า property X เป็น inherited property หรือไม่
- 5. ถ้า inherit ได้ — Browser มองขึ้นไปหา parent ที่ใกล้ที่สุดที่มีค่า property X แล้วใช้ค่านั้น
- 6. ถ้า inherit ไม่ได้ — Browser ใช้ค่า default ของ property นั้นแทน (เช่น margin ปกติเป็น 0)
- 7. ถ้าต้องการบังคับให้ inherit ก็สามารถใช้ค่า inherit ได้ เช่น border: inherit
ตัวอย่างเชิงเทคนิค — 3 กรณี
ดูตัวอย่าง 3 กรณีด้านล่าง แต่ละกรณีแสดงให้เห็นหลักการที่ต่างกัน: กรณีที่ 1: Inheritance พื้นฐาน p ภายใน .article ไม่มี color กำหนดโดยตรง แต่ได้รับมาจาก .article ผ่าน Inheritance กรณีที่ 2: Property ที่ไม่ inherit .article มี border และ padding แต่ p ภายในไม่ได้รับ border และ padding — เพราะ property เหล่านี้ไม่ inherit กรณีที่ 3: Cascade พื้นฐาน p ถูก match โดยทั้ง p { } และ .highlight { } — class selector ชนะเพราะ specificity สูงกว่า
/* ── กรณีที่ 1: Inheritance ── */
.article {
color: #1e40af; /* inherit ได้ */
font-family: Georgia, serif; /* inherit ได้ */
}
/* ผลลัพธ์: p ภายใน .article มีสีน้ำเงิน และใช้ Georgia โดยอัตโนมัติ */
/* ── กรณีที่ 2: Property ที่ไม่ inherit ── */
.card {
border: 2px solid #1e40af; /* ไม่ inherit */
padding: 24px; /* ไม่ inherit */
color: #1e40af; /* inherit ได้ */
}
/* ผลลัพธ์:
- p ภายใน .card → ได้ color (inherit)
- p ภายใน .card → ไม่มี border (ต้องกำหนดเอง)
- p ภายใน .card → ไม่มี padding (ต้องกำหนดเอง) */
/* ── กรณีที่ 3: Cascade ── */
p {
color: gray; /* specificity (0,0,0,1) */
}
.highlight {
color: #16a34a; /* specificity (0,0,1,0) — ชนะ! */
}
/* ผลลัพธ์: <p class="highlight"> → สีเขียว เพราะ .highlight มี specificity สูงกว่า p */จุดที่ผู้เริ่มต้นมักสับสน
- ❌ คิดว่า child จะรับทุก property จาก parent — จริง ๆ แล้วมีแค่ inherited properties เท่านั้นที่ส่งต่อได้
- ❌ สับสนระหว่าง Cascade กับ Inheritance — Cascade คือการแข่งขันของ rule, Inheritance คือการส่งค่าจาก parent สู่ child
- ❌ คิดว่า กฎที่อยู่ล่างสุดชนะเสมอ — จริงเฉพาะเมื่อ specificity เท่ากัน
- ❌ เห็น style เปลี่ยนแล้วไม่รู้ว่ามาจากไหน — ควรเปิด DevTools > Computed tab เพื่อดูต้นทาง
- ❌ สับสนระหว่าง Specificity กับ Cascade — Specificity เป็นแค่ปัจจัยหนึ่งใน Cascade ไม่ใช่คำเดียวกัน
- ❌ ไม่รู้ว่าปัญหาควรดู Cascade หรือ Inheritance — ถามตัวเองว่า 'element นี้มี rule ตรง ๆ ไหม' ถ้ามีคือ Cascade, ถ้าไม่มีแต่ยังได้ค่ามาคือ Inheritance
เปรียบเทียบ: Cascade vs Inheritance vs Specificity
สามเรื่องนี้เกี่ยวข้องกัน แต่เป็นคนละมิติ:
| หัวข้อ | Cascade | Inheritance | Specificity |
|---|---|---|---|
| คือ | กระบวนการตัดสินว่า rule ไหนชนะ | กลไกส่งค่าจาก parent สู่ child | น้ำหนักของ selector |
| ตอบคำถาม | กฎไหนมีผลจริง? | ค่านี้มาจากไหน? | selector ไหนเจาะจงกว่า? |
| สัมพันธ์กัน | ใช้ Specificity เป็นปัจจัยตัดสิน | Inheritance จะ 'แพ้' ถ้า element มี direct rule | เป็นส่วนหนึ่งของ Cascade |
| ตัวอย่าง | p vs .note → .note ชนะ | div { color: blue } → p ข้างในก็น้ำเงิน | #id (0,1,0,0) > .class (0,0,1,0) |
สรุปท้ายบท
จำสองประโยคนี้ไว้ก็พอ: แคสเคด (Cascade) = กฎไหนชนะ การสืบทอด (Inheritance) = ค่าไหนส่งต่อได้
- Cascade คือกระบวนการตัดสินว่าเมื่อ rule หลายอัน match element เดียวกัน rule ไหนจะมีผล
- Cascade ตัดสินโดย Specificity ก่อน — ถ้าเท่ากันดู Source Order (ทีหลังชนะ)
- Inheritance คือกลไกที่ element ลูกรับค่า property บางอย่างจาก parent อัตโนมัติ
- Property ที่ inherit ได้: color, font-family, font-size, line-height, text-align
- Property ที่ไม่ inherit: margin, padding, border, background, width, height, display
- ถ้า element มี rule ตรง ๆ → Cascade ตัดสิน (Inheritance ไม่เกี่ยว)
- ถ้า element ไม่มี rule ตรง ๆ → ดูว่า property นั้น inherit ได้ไหม แล้วจึงรับค่าจาก parent
- !important ข้ามกฎทั้งหมด — ใช้เฉพาะเมื่อจำเป็นจริง ๆ และเข้าใจผลที่ตามมา
Lab 1: Inheritance พื้นฐาน — Parent ส่ง Style ลงสู่ Child
เป้าหมาย: เข้าใจว่า element ลูกรับ color และ font-family จาก parent โดยอัตโนมัติ โจทย์: กำหนด color: #1e40af และ font-family: Georgia, serif ให้กับ .article แล้วสังเกตว่า h2 และ p ภายใน .article เปลี่ยนสีและ font โดยที่ไม่ต้องกำหนดให้แยก เงื่อนไข: — กำหนด color ใน .article เท่านั้น — ห้ามกำหนด color ให้ h2 หรือ p โดยตรง — กำหนด font-family ใน .article เท่านั้น แนวทางการคิด: color และ font-family เป็น inherited property — เมื่อกำหนดที่ parent, child ทุกตัวจะรับค่ามาโดยอัตโนมัติ
Lab 2: Property ที่ Inherit ได้ และ Property ที่ไม่ Inherit
เป้าหมาย: แยกแยะให้ได้ว่า property ไหน inherit ได้ และ property ไหนต้องกำหนดเองบน element ลูก โจทย์: .card ด้านล่างมี color, border, และ padding กำหนดไว้ครบ สังเกตว่า p.card-text ภายในได้รับ color มาอัตโนมัติ (Inheritance) แต่ไม่มี border และ padding ของตัวเอง งาน: กำหนด border: 1px dashed #1e40af และ padding: 8px ให้กับ .card-text โดยตรง เพื่อพิสูจน์ว่า border และ padding ต้องกำหนดเองเพราะไม่ inherit จาก parent เงื่อนไข: ห้ามแก้ไข .card — เพิ่มเฉพาะ .card-text
Lab 3: แก้ปัญหา Cascade และ Inheritance ร่วมกัน
เป้าหมาย: วิเคราะห์ว่า style ที่ element ได้รับมาจาก Cascade หรือ Inheritance และแก้ปัญหาโดยใช้ความเข้าใจทั้งสองเรื่อง บริบท: — p { color: gray } — element selector กำหนดสีเทาให้ทุก p — .content { color: #1e40af } — parent กำหนดสีน้ำเงิน — p.message ปัจจุบันเป็นสีเทา เพราะ Cascade ตัดสินให้ p { } ชนะ (direct rule ชนะ inheritance) โจทย์: แก้ให้ p.message มีสี #1e40af (น้ำเงิน) โดยเพิ่ม color: #1e40af ให้กับ .message selector เงื่อนไข: — ห้ามใช้ !important — ห้ามแก้ไข p { } rule — ต้องเพิ่ม .message { color: #1e40af } (class selector มี specificity สูงกว่า element selector) แนวทางการคิด: ปัญหาเกิดจาก Cascade — p { color: gray } ชนะ .content { color: #1e40af } เพราะเป็น direct rule การแก้คือเพิ่ม rule สำหรับ .message ที่มี specificity สูงกว่า p