CSS
Layout Basics
z-index
เรียนรู้วิธีควบคุมลำดับการซ้อนทับของ element — element ใดควรอยู่ด้านหน้า element ใดควรอยู่ด้านหลัง และทำไม z-index ถึงต้องใช้คู่กับ position เสมอ
หัวข้อนี้คืออะไร
z-index คือ CSS property ที่ใช้กำหนด "ลำดับซ้อนทับ (stacking order)" ของ element — ว่า element ใดจะปรากฏอยู่ด้านหน้า และ element ใดจะอยู่ด้านหลัง เมื่อ element หลายตัวซ้อนทับกันในพื้นที่เดียวกัน โดยปกติ browser จะแสดง element ตามลำดับ HTML — element ที่อยู่หลังใน HTML จะแสดงทับ element ที่อยู่ก่อน แต่เมื่อใช้ z-index คุณสามารถเปลี่ยนลำดับนั้นได้ ⚠️ สิ่งสำคัญที่ต้องรู้: z-index จะทำงานก็ต่อเมื่อ element นั้นมี position ที่ไม่ใช่ static (เช่น relative, absolute, fixed, sticky) เท่านั้น
- z-index คือตัวเลขที่บอกว่า element อยู่ 'ชั้นที่เท่าไหร่' ในแกน Z (ความลึก)
- ยิ่งตัวเลขมาก element จะยิ่งอยู่ด้านหน้า (แสดงทับ element ที่มีค่าน้อยกว่า)
- ค่าเป็นได้ทั้งบวก, ลบ, หรือ 0
- ต้องใช้คู่กับ position: relative, absolute, fixed, หรือ sticky เสมอ
- ถ้าไม่มี position (หรือเป็น static) z-index จะไม่มีผล
ทำไมหัวข้อนี้จึงสำคัญ
ในงาน frontend จริง เราสร้าง UI component หลายชิ้นที่ต้องซ้อนทับกัน ถ้าไม่เข้าใจ z-index คุณจะแก้ปัญหา "element บังกัน" ไม่ถูก ตัวอย่าง UI ที่ต้องพึ่งพา z-index: - Modal (หน้าต่างลอย) — ต้องอยู่เหนือทุกอย่างในหน้า - Dropdown menu — ต้องลอยอยู่เหนือ element ที่อยู่ใต้มัน - Tooltip — ต้องแสดงทับ element อื่นชั่วคราว - Badge / ป้ายแจ้งเตือน — ต้องอยู่มุมของ element หลัก - Overlay / ม่านพื้นหลัง — ต้องบังเนื้อหาด้านหลัง - Sticky header — ต้องอยู่เหนือเนื้อหาเมื่อ scroll ลง ปัญหาที่เกิดขึ้นบ่อยโดยไม่รู้สาเหตุ: - กด dropdown แล้ว menu หายไปหลัง element อื่น - Modal แสดงอยู่แต่ถูกปุ่มหรือ header บัง - Badge ถูกรูปภาพทับจนมองไม่เห็น
ตัวอย่างจากชีวิตจริง
ลองนึกภาพกองกระดาษบนโต๊ะ: 📄 กระดาษหลายแผ่นซ้อนกัน เมื่อวางกระดาษหลายแผ่นซ้อนกัน แผ่นที่อยู่บนสุดจะถูกมองเห็นก่อน แผ่นที่อยู่ด้านล่างจะถูกบัง — นี่คือหลักการเดียวกับ z-index ใน CSS 🔢 z-index คือ "ตำแหน่งชั้น" ถ้าคุณเขียนเลขบนกระดาษแต่ละแผ่น (1, 2, 3, ...) แผ่นที่มีตัวเลขมากที่สุดจะอยู่บนสุดเสมอ ไม่ว่าจะวางลำดับไหนก่อน 📌 ข้อแม้สำคัญ: กระดาษต้อง "ลอยอยู่" ก่อน ถ้ากระดาษถูกยึดติดกับพื้นโต๊ะ (position: static) ตัวเลขบนกระดาษจะไม่มีความหมาย กระดาษต้อง "ถูกหยิบขึ้นมา" ก่อน (มี position ที่ไม่ใช่ static) จึงจะสามารถเรียงลำดับตามตัวเลขได้ 🎴 ตัวอย่างจริง: ไพ่ในเกมไพ่ เมื่อแจกไพ่บนโต๊ะ ไพ่แต่ละใบซ้อนกัน ไพ่ที่ "หยิบขึ้นมา" (selected) จะต้องอยู่บนสุด ส่วนไพ่ที่วางทับกันตามปกติก็เรียงตาม DOM order
แนวคิดหลักที่ต้องเข้าใจ
z-index ต้องการ position ถึงจะทำงาน — ยิ่งค่ามาก ยิ่งอยู่ด้านหน้า
มีสามแนวคิดสำคัญที่ต้องเข้าใจก่อนใช้ z-index: ── 1. z-index ต้องใช้คู่กับ position ── z-index จะไม่มีผลเลยถ้า element มี position: static (ค่า default) ต้องกำหนด position: relative, absolute, fixed, หรือ sticky ก่อน ── 2. ค่ายิ่งมาก ยิ่งอยู่หน้า ── z-index: 100 อยู่หน้า z-index: 10 z-index: 10 อยู่หน้า z-index: 1 z-index: 1 อยู่หน้า z-index: auto (ค่าเริ่มต้น) z-index: -1 อยู่หลัง element ทั่วไป ── 3. การเปรียบเทียบอยู่ในบริบทเดียวกัน (Stacking Context) ── z-index เปรียบเทียบกับ element ที่อยู่ใน stacking context เดียวกัน element จาก stacking context ต่างกันไม่แข่งขันกันโดยตรง (แนวคิดนี้ลงลึกมากขึ้นในบทขั้นสูง สำหรับตอนนี้ให้เข้าใจเบื้องต้นว่า z-index ต้องมี position ก่อน)
การทำงานทีละขั้นตอน
เมื่อ browser ต้องแสดง element ที่ซ้อนกัน นี่คือสิ่งที่เกิดขึ้น:
- ขั้นที่ 1 — browser วาง element ตามลำดับ HTML (DOM order): element ที่อยู่ทีหลังใน HTML จะแสดงทับ element ที่อยู่ก่อน นี่คือพฤติกรรม default
- ขั้นที่ 2 — ถ้า element มี position ≠ static: element นั้นจะสามารถรับค่า z-index และเริ่มแข่งขันลำดับการซ้อนทับ
- ขั้นที่ 3 — browser เปรียบเทียบค่า z-index: ใน stacking context เดียวกัน element ที่มี z-index มากกว่าจะแสดงอยู่ด้านหน้า
- ขั้นที่ 4 — ถ้า z-index เท่ากัน: browser ใช้ลำดับ HTML (element ที่อยู่หลังกว่าในโค้ดจะอยู่หน้า)
- ขั้นที่ 5 — ถ้าไม่มี z-index (auto): element จะเรียงตาม DOM order ใน stacking context ของ parent
ตัวอย่างโค้ด
ดูการใช้ z-index กับ element ที่ซ้อนกัน 3 ชิ้น:
ทั้ง 3 กล่องมี position: absolute และ z-index ต่างกัน — กล่องที่มี z-index มากกว่าจะแสดงทับกล่องที่มีค่าน้อยกว่า
/* กล่อง 3 ใบ วางซ้อนกันในตำแหน่งเดียว */
.container {
position: relative; /* กำหนด containing block */
width: 300px;
height: 200px;
}
/* กล่องหลัง — z-index ต่ำสุด */
.box-back {
position: absolute;
top: 0; left: 0;
width: 200px; height: 140px;
background: #cbd5e1;
z-index: 10; /* ← อยู่หลังสุด */
}
/* กล่องกลาง */
.box-mid {
position: absolute;
top: 30px; left: 30px;
width: 200px; height: 140px;
background: #93c5fd;
z-index: 20; /* ← อยู่กลาง */
}
/* กล่องหน้า — z-index สูงสุด */
.box-front {
position: absolute;
top: 60px; left: 60px;
width: 200px; height: 140px;
background: #6366f1;
z-index: 30; /* ← อยู่หน้าสุด */
}ทดลองเปลี่ยน z-index
จุดที่ผู้เริ่มต้นมักสับสน
ตรวจสอบว่าคุณเข้าใจถูกต้องหรือยัง:
- ❌ ใส่ z-index แล้วไม่มีผล — สาเหตุที่พบบ่อยที่สุด: element นั้นยังมี position: static (ค่า default) ต้องเพิ่ม position: relative, absolute, fixed หรือ sticky ก่อนเสมอ
- ❌ คิดว่าตัวเลขมากที่สุดชนะทุกสถานการณ์ — ไม่จริงเสมอไป เพราะ z-index เปรียบเทียบกันเฉพาะใน stacking context เดียวกัน element ที่อยู่ใน stacking context ต่างกันจะไม่แข่งกันโดยตรง
- ❌ สับสนระหว่าง z-index กับ position — position กำหนด 'ที่ตั้ง' (x, y บนหน้าจอ) ส่วน z-index กำหนด 'ลำดับซ้อน' (ใครอยู่หน้า/หลัง) ทั้งสองต่างกันโดยสิ้นเชิง
- ❌ ใช้ตัวเลข z-index ใหญ่มากเกินจำเป็น เช่น z-index: 9999 — ทำให้โค้ดบำรุงรักษายาก ควรใช้ตัวเลขที่มีระบบ เช่น 10, 20, 30 หรือ 100, 200, 300 เพื่อให้มีพื้นที่แทรกค่ากลางในอนาคต
- ❌ ลืมว่า z-index: auto กับ z-index: 0 ต่างกัน — auto หมายถึงไม่สร้าง stacking context ใหม่ ส่วน 0 สร้าง stacking context ใหม่ (เรื่องนี้สำคัญในระดับขั้นสูง)
เปรียบเทียบสิ่งที่ใกล้เคียง
ความแตกต่างระหว่าง z-index, position, และลำดับ DOM:
| แนวคิด | กำหนดอะไร | แกนที่ใช้ | ต้องการ position |
|---|---|---|---|
| DOM order (ลำดับ HTML) | ลำดับเริ่มต้นของการแสดงผล | Z (ซ้อนทับ) | ไม่ |
| position: top/left | ตำแหน่ง X, Y บนหน้าจอ | X, Y | ใช่ (position ≠ static) |
| z-index | ลำดับซ้อนทับ (ใครอยู่หน้า/หลัง) | Z | ใช่ (position ≠ static) |
| opacity: 0 | ซ่อน element แต่ยังกินพื้นที่ | — | ไม่ |
| display: none | ซ่อน element และไม่กินพื้นที่ | — | ไม่ |
สรุปท้ายบท
จำหลักการนี้ไว้: "z-index บอกว่า element อยู่ชั้นที่เท่าไหร่ในแกน Z — แต่ต้องมี position ก่อนถึงจะใช้ได้"
- z-index ใช้กำหนดลำดับการซ้อนทับ — ค่ายิ่งมาก ยิ่งอยู่ด้านหน้า
- z-index ต้องใช้คู่กับ position: relative, absolute, fixed, หรือ sticky เท่านั้น
- ถ้า z-index ไม่ทำงาน ให้ตรวจสอบ position ก่อนเสมอ
- ค่า z-index เป็นได้ทั้งบวก (อยู่หน้า), ลบ (อยู่หลัง element ปกติ), หรือ 0
- ใช้ตัวเลขที่มีระบบ เช่น 10, 20, 30 เพื่อให้บำรุงรักษาง่าย
- z-index ไม่ใช่การกำหนดตำแหน่ง (x, y) — นั่นคือหน้าที่ของ top, right, bottom, left
- เมื่อ z-index ไม่ทำงานในกรณีซับซ้อน มักเกิดจาก stacking context (เรียนได้ในบทขั้นสูง)
Lab 1 — ทำให้ element อยู่ด้านหน้า
เป้าหมาย: ฝึกใช้ position และ z-index เพื่อควบคุมลำดับซ้อนทับ โจทย์: .content มี position: absolute และ z-index: 5 อยู่แล้ว .overlay ตอนนี้ไม่มี position (static) จึงถูก .content บัง กำหนด position: relative และ z-index: 10 ให้กับ .overlay เพื่อให้มันลอยอยู่เหนือ .content ระบบจะตรวจ: 1. .overlay มี position: relative ใน CSS 2. .overlay มี z-index: 10 ใน CSS 3. computed z-index ของ .overlay = 10 ใน browser 4. z-index ของ .overlay มากกว่า .content เงื่อนไข: ห้ามเปลี่ยน HTML และห้ามแก้ CSS ของ .content
Lab 2 — เรียงลำดับกล่อง 3 ชั้น
เป้าหมาย: ฝึกกำหนด z-index ให้ element หลายตัวเรียงลำดับได้ตามต้องการ โจทย์: มีกล่อง 3 ใบ (.box-back, .box-mid, .box-front) ที่วางซ้อนกันอยู่ ทุกกล่องมี position: absolute แล้ว แต่ยังไม่มี z-index กำหนด z-index ให้กล่องเรียงตามลำดับนี้: - .box-front — z-index: 30 (อยู่หน้าสุด) - .box-mid — z-index: 20 (อยู่กลาง) - .box-back — z-index: 10 (อยู่หลังสุด) ระบบจะตรวจ: 1. .box-front มี z-index: 30 2. .box-mid มี z-index: 20 3. .box-back มี z-index: 10 4. ลำดับ front > mid > back ถูกต้องใน browser เงื่อนไข: ห้ามเปลี่ยน HTML
Lab 3 — แก้ dropdown ถูกบัง
เป้าหมาย: แก้ปัญหาในโลกจริง — dropdown menu ถูก element อื่นบัง โจทย์: .card มี position: relative และ z-index: 20 .dropdown คือเมนูที่ควรลอยอยู่เหนือทุกอย่าง แต่ตอนนี้ถูก .card บัง เพราะ .dropdown ยังไม่มี z-index กำหนด z-index: 50 ให้กับ .dropdown เพื่อให้มันลอยอยู่เหนือ .card ระบบจะตรวจ: 1. .dropdown มี z-index: 50 ใน CSS 2. z-index ของ .dropdown มากกว่า .card ใน browser เงื่อนไข: ห้ามเปลี่ยน HTML และห้ามแก้ CSS ของ .card