CSS
Layout Basics
Position
เรียนรู้วิธีควบคุมตำแหน่งของ element ใน CSS — ตั้งแต่การวางตามปกติ ขยับจากตำแหน่งเดิม ลอยอิสระในกรอบอ้างอิง ติดกับหน้าจอ ไปจนถึงการติดเมื่อเลื่อนถึงจุดที่กำหนด
หัวข้อนี้คืออะไร
position (ตำแหน่ง) คือ CSS property ที่ใช้กำหนด "วิธีที่ browser วาง element ในหน้าเว็บ" โดยปกติ element ทุกตัวถูกวางตาม normal flow — เรียงกันจากบนลงล่าง ซ้ายไปขวา ตามลำดับใน HTML แต่เมื่อกำหนด position คุณสามารถเปลี่ยนพฤติกรรมนั้นได้ เมื่อใช้ position ร่วมกับ top, right, bottom, left คุณสามารถกำหนดว่า element อยู่ตรงไหนบนหน้าเว็บได้อย่างแม่นยำ ค่าของ position มีห้าแบบ:
- static (สแตติก) — ค่า default: วางตาม normal flow ปกติ ไม่ได้รับผลจาก top/right/bottom/left
- relative (รีเลทีฟ) — วางตาม normal flow ก่อน แล้วขยับจากตำแหน่งเดิมด้วย top/right/bottom/left
- absolute (แอบโซลูต) — หลุดออกจาก normal flow อิงตำแหน่งกับ ancestor ที่ใกล้ที่สุดที่ไม่ใช่ static
- fixed (ฟิกซ์) — หลุดออกจาก normal flow อิงตำแหน่งกับ viewport เลื่อนหน้าไม่ขยับ
- sticky (สติกกี) — เหมือน relative จนกว่าจะเลื่อนถึงเงื่อนไขที่กำหนด แล้วจะ 'ติด' เหมือน fixed
ทำไมหัวข้อนี้จึงสำคัญ
position เป็นหนึ่งใน property ที่ใช้บ่อยที่สุดในงาน frontend จริง ถ้าไม่เข้าใจ position คุณจะไม่สามารถสร้างสิ่งเหล่านี้ได้: - ป้าย (badge) มุมกล่องหรือ avatar เช่น ตัวเลขแจ้งเตือนที่มุมกระดิ่ง - Modal หรือ dialog ที่ลอยอยู่กลางหน้าจอ - Tooltip ที่ปรากฏเหนือหรือใต้ element - Sticky header ที่ติดอยู่ด้านบนเมื่อเลื่อนหน้า - ปุ่มลอย (floating button) ที่ติดมุมหน้าจอเสมอ - Dropdown menu ที่ซ้อนทับบน element อื่น การเข้าใจ position ช่วยให้คุณ: - วาง element ได้ตรงตำแหน่งที่ต้องการอย่างแม่นยำ - สร้าง UI component ที่ซับซ้อนขึ้นได้ - แก้ปัญหา element ที่ไม่อยู่ในตำแหน่งที่คาดหวัง
ตัวอย่างจากชีวิตจริง
ลองนึกภาพการจัดวางของในห้องทำงาน: 📌 position: static — วางตามลำดับปกติ โต๊ะ เก้าอี้ ชั้นหนังสือ วางเรียงกันตามลำดับที่นำเข้ามาในห้อง ไม่มีการขยับพิเศษ → ทุก element เริ่มต้นด้วยค่านี้ — วางตาม flow ของ HTML ↕️ position: relative — ขยับจากตำแหน่งเดิม โต๊ะอยู่ตำแหน่งปกติ แต่คุณเลื่อนมันไปทางขวา 20 ซม. จากตำแหน่งเดิม — ที่เดิมยังถูก "จอง" ไว้ ของข้างๆ ไม่เลื่อนตาม → element ยังกินพื้นที่ใน flow แต่มองเห็นเลื่อนไปจากจุดเดิม 📍 position: absolute — ลอยอิสระในกรอบอ้างอิง สติกเกอร์ที่ติดบนไวท์บอร์ด — มันไม่ได้อยู่ใน "ลำดับ" ของของในห้อง แต่อิงตำแหน่งกับกรอบไวท์บอร์ด สติกเกอร์ถูกวางไว้ที่มุมบนขวาของไวท์บอร์ดพอดี → หลุดออกจาก flow, อิงกับ parent ที่ไม่ใช่ static ที่ใกล้ที่สุด 📺 position: fixed — ติดกับหน้าจอ รีโมตที่ติดอยู่กับขอบโต๊ะด้วยกาว ไม่ว่าคุณจะเดินไปไหนในห้อง รีโมตยังอยู่ที่เดิมเสมอ → อิงกับ viewport, ไม่เลื่อนตามหน้า 🧲 position: sticky — ปกติไหลตาม flow แต่ "ติด" เมื่อถึงจุดหนึ่ง กระดาษโน้ตที่ขณะปกติวางอยู่ในกองเอกสาร แต่พอคุณยกขึ้นถึงระดับสายตา มันติดค้างอยู่ตรงนั้นจนกว่าจะวางลง → ทำงานเหมือน relative จนกว่า scroll จะถึง threshold แล้วค้างไว้เหมือน fixed
แนวคิดหลักที่ต้องเข้าใจ
static = ปกติ | relative = ขยับจากเดิม | absolute = ลอยในกรอบ | fixed = ติดหน้าจอ | sticky = ติดเมื่อเลื่อน
ก่อนดูโค้ด มีสองแนวคิดสำคัญที่ต้องรู้จัก: ── Normal Flow ── คือวิธีที่ browser วาง element ตามปกติโดยไม่มีการแทรกแซง — block element วางซ้อนกันจากบนลงล่าง inline element เรียงในบรรทัดเดียวกัน position: static คือการวางใน normal flow นี้ ── Containing Block ── เมื่อใช้ position: absolute — element จะอิงตำแหน่งกับ "containing block" ซึ่งคือ ancestor ที่ใกล้ที่สุดที่มี position ที่ไม่ใช่ static (เช่น relative, absolute, fixed, sticky) ถ้าไม่มี ancestor แบบนั้น จะอิงกับ viewport แทน ── top / right / bottom / left ── ใช้ระบุว่า element อยู่ห่างจากขอบไหนเท่าไหร่ เช่น: - top: 20px = ห่างจากขอบบน 20px - right: 0 = ชิดขอบขวา - bottom: 16px = ห่างจากขอบล่าง 16px - left: 50% = ห่างจากขอบซ้าย 50% ⚠️ สำหรับ static: top/right/bottom/left ไม่มีผล ⚠️ สำหรับ absolute/fixed: element หลุดออกจาก normal flow ไม่กินพื้นที่ใน layout
การทำงานทีละขั้นตอน
สำหรับแต่ละค่าของ position — นี่คือสิ่งที่ browser ทำ:
- static: browser วาง element ตาม normal flow โดยไม่มีการปรับเปลี่ยน top/right/bottom/left ถูกเพิกเฉย — พฤติกรรมนี้เกิดขึ้นกับทุก element โดย default
- relative: browser วาง element ตาม normal flow ก่อน (พื้นที่เดิมยังถูกจอง) จากนั้นขยับ element ให้ห่างจากตำแหน่งเดิมตามค่า top/right/bottom/left — element อื่นไม่รับผล
- absolute: browser นำ element ออกจาก normal flow ทั้งหมด (ไม่กินพื้นที่) แล้ววางอิงกับ containing block ที่ใกล้ที่สุด ถ้าไม่มี parent ที่เป็น non-static จะอิงกับ <html> element
- fixed: browser นำ element ออกจาก normal flow และวางอิงกับ viewport เสมอ เลื่อนหน้าเพจไม่กระทบตำแหน่ง — ใช้สำหรับ header ที่ติดอยู่ด้านบน หรือปุ่มลอย
- sticky: browser วาง element ตาม normal flow (เหมือน relative) จนกว่าผู้ใช้จะ scroll จน element ถึงค่า top/bottom ที่กำหนด เมื่อนั้น element จะ 'ติด' ค้างไว้ และจะกลับไปตาม flow เมื่อ scroll กลับ
ตัวอย่างเชิงเทคนิค — โค้ดจริง
ดูตัวอย่างที่ใช้บ่อยในงานจริง:
จำไว้: absolute ต้องมี parent ที่ position ไม่ใช่ static เสมอ มิฉะนั้น element จะอิงกับ <html> ซึ่งมักให้ผลที่ไม่คาดหวัง
/* 1. static — ค่า default (ไม่ต้องเขียนก็ได้) */
.normal-box {
position: static;
/* top/left ไม่มีผล */
}
/* 2. relative — ขยับจากตำแหน่งเดิม */
.shifted-box {
position: relative;
top: 20px; /* ขยับลง 20px จากตำแหน่งปกติ */
left: 30px; /* ขยับขวา 30px จากตำแหน่งปกติ */
/* พื้นที่เดิมยังถูกจองไว้ */
}
/* 3. absolute — ลอยในกรอบ parent */
.parent {
position: relative; /* สำคัญ! ต้องกำหนดให้ parent */
width: 200px;
height: 120px;
}
.badge {
position: absolute;
top: 8px; /* ห่างจากขอบบนของ parent 8px */
right: 8px; /* ห่างจากขอบขวาของ parent 8px */
/* หลุดจาก flow, ไม่กินพื้นที่ใน layout */
}
/* 4. fixed — ติดกับ viewport */
.back-to-top {
position: fixed;
bottom: 24px; /* ห่างจากขอบล่างหน้าจอ 24px */
right: 24px; /* ห่างจากขอบขวาหน้าจอ 24px */
/* ไม่ขยับตามการ scroll */
}
/* 5. sticky — ติดเมื่อ scroll ถึง */
.section-header {
position: sticky;
top: 0; /* ติดที่ขอบบนเมื่อ scroll ถึง */
/* ก่อนถึงจุดนั้น ทำงานเหมือน relative */
}ดูความแตกต่างแบบ Interactive
ทดลองเปลี่ยนค่า position ของ .box แล้วสังเกตว่า element ขยับต่างกันอย่างไร:
จุดที่ผู้เริ่มต้นมักสับสน
ตรวจสอบว่าคุณเข้าใจถูกต้องหรือยัง:
- ❌ ลืมกำหนด parent ให้เป็น relative ก่อนใช้ absolute — ถ้า parent ทุกตัวเป็น static ลูกที่เป็น absolute จะอิงกับ <html> element ทั้งหน้า ทำให้วางผิดตำแหน่ง แก้ไขด้วยการเพิ่ม position: relative ให้ parent ที่ต้องการ
- ❌ คิดว่า top/left ใช้ได้กับทุก position — top, right, bottom, left ไม่มีผลกับ position: static เลย ต้องใช้ relative, absolute, fixed หรือ sticky เท่านั้น
- ❌ สับสนว่า relative ขยับแล้ว element อื่นขยับตาม — ไม่ใช่ relative ขยับแค่ตัวเอง (visual) แต่พื้นที่เดิมใน flow ยังถูกจองไว้ element รอบข้างไม่ได้รับผล
- ❌ คิดว่า sticky เหมือน fixed ตลอดเวลา — sticky ทำงานเหมือน relative ก่อน และจะ 'ติด' เฉพาะเมื่อ scroll ผ่าน threshold ที่กำหนด นอกจากนี้ sticky ยังถูกจำกัดด้วยขอบของ parent container
- ❌ absolute element ล้นออกนอก parent — ถ้า parent ไม่มี overflow: hidden absolute element สามารถแสดงผลนอก parent ได้ เพราะมันหลุดจาก normal flow แล้ว
เปรียบเทียบค่า position
สรุปความแตกต่างหลักของ position แต่ละค่า:
| ค่า | อยู่ใน Normal Flow | อิงกับ | top/left มีผล | เลื่อนตาม scroll |
|---|---|---|---|---|
| static | ใช่ | — | ไม่ | ใช่ |
| relative | ใช่ (พื้นที่จอง) | ตำแหน่งเดิมของตัวเอง | ใช่ | ใช่ |
| absolute | ไม่ | containing block (non-static parent) | ใช่ | ใช่ (ถ้า parent ไม่ fixed) |
| fixed | ไม่ | viewport | ใช่ | ไม่ |
| sticky | ใช่ (จนถึง threshold) | ตัวเอง → viewport เมื่อถึง threshold | ใช่ | บางส่วน |
สรุปท้ายบท
จำง่ายๆ แบบนี้: "position กำหนดว่า element อิงตำแหน่งกับ 'อะไร' และอยู่ใน normal flow หรือไม่"
- static — วางตาม flow ปกติ ค่า default ของทุก element top/left ไม่มีผล
- relative — ยังอยู่ใน flow แต่ขยับจากตำแหน่งเดิมได้ พื้นที่เดิมยังถูกจอง
- absolute — หลุดจาก flow อิงกับ non-static parent ที่ใกล้ที่สุด ต้องมี parent เป็น relative
- fixed — หลุดจาก flow อิงกับ viewport ไม่ขยับตาม scroll เหมาะสำหรับ header ลอย, ปุ่ม FAB
- sticky — เหมือน relative แต่ 'ติด' เมื่อ scroll ถึง threshold เหมาะสำหรับ sticky header, sidebar
- top/right/bottom/left ใช้ได้กับทุกค่ายกเว้น static
- absolute ต้องมี parent ที่ position ≠ static มิฉะนั้นอิงกับ <html>
Lab 1 — ขยับ element ด้วย relative
เป้าหมาย: ฝึกใช้ position: relative ร่วมกับ top และ left โจทย์: .box ตอนนี้วางตาม normal flow ปกติ กำหนด position: relative พร้อม top: 20px และ left: 40px ให้กับ .box ระบบจะตรวจ: 1. .box มี position: relative ใน CSS 2. .box มี top: 20px ใน CSS 3. .box มี left: 40px ใน CSS 4. computed position ใน browser = relative 5. ตำแหน่ง offsetTop ของ .box ขยับไปจากตำแหน่งเดิม เงื่อนไข: ห้ามเปลี่ยน HTML และห้ามใช้ position ค่าอื่น
Lab 2 — วางป้ายมุมกล่องด้วย absolute
เป้าหมาย: ฝึกวาง element แบบ absolute ภายใน parent ที่เป็น relative โจทย์: .card คือการ์ดสินค้า เพิ่ม .badge (ป้ายแจ้งเตือน) ที่มุมบนขวาของ .card โดย: 1. กำหนด position: relative ให้ .card 2. กำหนด position: absolute พร้อม top: 8px และ right: 8px ให้ .badge ระบบจะตรวจ: 1. .card มี position: relative 2. .badge มี position: absolute 3. .badge มี top: 8px และ right: 8px 4. computed position ของทั้งคู่ถูกต้อง 5. .badge อยู่ในมุมบนขวาของ .card จริง เงื่อนไข: ต้องใช้ absolute ไม่ใช่ fixed หรือ relative
Lab 3 — สร้างปุ่มลอยด้วย fixed
เป้าหมาย: ฝึกใช้ position: fixed เพื่อสร้างปุ่มที่ติดกับหน้าจอ โจทย์: สร้างปุ่ม "กลับด้านบน" (.back-to-top) ที่ติดอยู่ที่มุมล่างขวาของหน้าจอเสมอ กำหนด position: fixed พร้อม bottom: 24px และ right: 24px ให้กับ .back-to-top ระบบจะตรวจ: 1. .back-to-top มี position: fixed ใน CSS 2. .back-to-top มี bottom: 24px ใน CSS 3. .back-to-top มี right: 24px ใน CSS 4. computed position = fixed ใน browser 5. ตำแหน่งของ .back-to-top อยู่ที่มุมล่างขวาของ viewport เงื่อนไข: ต้องใช้ fixed ไม่ใช่ absolute หรือ sticky