Article · programming
HTML & CSS EP8: CSS Grid — สร้าง Layout เว็บไซต์แบบหลายคอลัมน์อย่างมืออาชีพ
HTML & CSS
เรียนรู้ CSS Grid ตั้งแต่การกำหนดแถวและคอลัมน์ การใช้ fr, repeat(), minmax(), gap, grid-template-areas และการออกแบบ Responsive Layout พร้อมตัวอย่างหน้าเว็บ HTML เต็มรูปแบบที่สามารถทดลองได้ทันที
1. CSS Grid คืออะไร และเหมาะกับงานแบบไหน
CSS Grid เป็นระบบจัดวางองค์ประกอบบนหน้าเว็บที่ช่วยควบคุมทั้งแถวและคอลัมน์ได้พร้อมกัน เหมาะกับงานที่ต้องจัดโครงสร้างเป็นตาราง เช่น หน้า Dashboard, แกลเลอรีรูปภาพ, หน้ารวมสินค้า, หน้าแสดงบทความ และ Layout ที่มีส่วนหัว เมนู เนื้อหา และแถบด้านข้าง
เมื่อเทียบกับ Flexbox ซึ่งเหมาะกับการจัดองค์ประกอบตามแกนหลักหนึ่งมิติ CSS Grid ช่วยกำหนดความสัมพันธ์ของแถวและคอลัมน์ในสองมิติได้สะดวกกว่า ทั้งสองเทคนิคสามารถใช้ร่วมกันได้ เช่น ใช้ Grid จัดตำแหน่งการ์ดสินค้า และใช้ Flexbox จัดปุ่มหรือเนื้อหาภายในแต่ละการ์ด
💡 แนวคิดสำคัญใช้ CSS Grid เมื่อจำเป็นต้องควบคุมโครงสร้างแถวและคอลัมน์ ใช้ Flexbox เมื่อเน้นการจัดเรียงรายการตามแกนเดียว และเลือกใช้ทั้งสองร่วมกันได้ตามความเหมาะสม
2. เริ่มต้นใช้งาน CSS Grid
การเปิดใช้งาน Grid ทำได้โดยกำหนด `display: grid` ให้กับองค์ประกอบแม่ที่เรียกว่า Grid Container ส่วนองค์ประกอบลูกโดยตรงจะกลายเป็น Grid Items และถูกจัดวางตามกฎของ Grid โดยอัตโนมัติ
<div class="grid-container">
<div class="item">รายการ 1</div>
<div class="item">รายการ 2</div>
<div class="item">รายการ 3</div>
<div class="item">รายการ 4</div>
</div>
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.item {
padding: 24px;
background: #dbeafe;
border: 1px solid #93c5fd;
border-radius: 10px;
}
ตัวอย่างนี้สร้าง Grid จำนวนสองคอลัมน์ที่มีขนาดเท่ากัน โดย `1fr 1fr` หมายถึงแบ่งพื้นที่ที่มีอยู่เป็นสองส่วนเท่า ๆ กัน ส่วน `gap: 16px` กำหนดระยะห่างระหว่างแถวและคอลัมน์ หากมีรายการมากกว่าสองรายการ รายการที่เกินจะไหลไปยังแถวถัดไปโดยอัตโนมัติ
3. กำหนดคอลัมน์ด้วย grid-template-columns
คุณสมบัติ `grid-template-columns` ใช้กำหนดจำนวนและขนาดของคอลัมน์ สามารถใช้หน่วยพิกเซล เปอร์เซ็นต์ หน่วย `fr` หรือฟังก์ชันอย่าง `repeat()` และ `minmax()` เพื่อสร้าง Layout ที่ยืดหยุ่นตามพื้นที่ของหน้าจอ
| รูปแบบ | ความหมาย | ตัวอย่างการใช้งาน |
|---|---|---|
| 200px 200px | สองคอลัมน์ กว้างคอลัมน์ละ 200px | กล่องที่ต้องการขนาดคงที่ |
| 1fr 1fr | แบ่งพื้นที่เป็นสองส่วนเท่ากัน | การ์ดสองคอลัมน์ |
| 1fr 2fr | แบ่งพื้นที่เป็นหนึ่งส่วนและสองส่วน | เนื้อหาหลักกับแถบข้าง |
| repeat(3, 1fr) | สร้างสามคอลัมน์ที่เท่ากัน | แกลเลอรีหรือรายการสินค้า |
| repeat(4, minmax(0, 1fr)) | สร้างสี่คอลัมน์ โดยแต่ละคอลัมน์ยืดหยุ่นและย่อได้ | ตารางการ์ดบนหน้าจอขนาดใหญ่ |
3.1 หน่วย fr คืออะไร
หน่วย `fr` ย่อมาจาก fractional unit ใช้แบ่งพื้นที่ว่างที่เหลือใน Grid ตามสัดส่วน เช่น `1fr 2fr` หมายถึงคอลัมน์ที่สองได้รับพื้นที่ตามสัดส่วนสองเท่าของคอลัมน์แรก ภายใต้พื้นที่ที่ Grid สามารถจัดสรรได้ หลังคำนึงถึงข้อจำกัดของขนาดและช่องว่างที่กำหนด
.layout {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
}
.sidebar {
background: #e0f2fe;
}
.main-content {
background: #f1f5f9;
}
3.2 ใช้ repeat() ลดการเขียนโค้ดซ้ำ
หากต้องการคอลัมน์หลายช่องที่มีขนาดเท่ากัน ไม่จำเป็นต้องเขียน `1fr` ซ้ำหลายครั้ง สามารถใช้ `repeat()` เพื่อให้ CSS สั้นลงและแก้ไขได้ง่ายขึ้น ตัวอย่าง `repeat(3, 1fr)` เทียบเท่ากับ `1fr 1fr 1fr`
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
3.3 ใช้ minmax() ควบคุมขนาดต่ำสุดและสูงสุด
ฟังก์ชัน `minmax(min, max)` ใช้กำหนดขอบเขตขนาดของแถวหรือคอลัมน์ ตัวอย่าง `minmax(220px, 1fr)` กำหนดให้คอลัมน์มีขนาดอย่างน้อย 220 พิกเซล และสามารถขยายเพื่อใช้พื้นที่ที่เหลือได้ ทั้งนี้จำนวนคอลัมน์ที่สร้างได้ยังขึ้นอยู่กับความกว้างของ Container และระยะห่างระหว่างคอลัมน์
.product-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 20px;
}
@media (max-width: 760px) {
.product-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 480px) {
.product-grid {
grid-template-columns: 1fr;
}
}
ข้อควรระวังเรื่องขนาดหากต้องการให้จำนวนคอลัมน์เปลี่ยนตามพื้นที่โดยอัตโนมัติ ควรเลือกใช้ `auto-fit` หรือ `auto-fill` ร่วมกับ `minmax()` ให้เหมาะสม และอย่าลืมตรวจสอบความกว้างขั้นต่ำของเนื้อหา เพราะข้อความยาวหรือรูปภาพที่ไม่มีข้อจำกัดด้านความกว้างอาจทำให้ Layout ล้นได้
4. กำหนดระยะห่างด้วย gap
คุณสมบัติ `gap` ใช้กำหนดช่องว่างระหว่างแถวและคอลัมน์ใน Grid โดยไม่เพิ่มระยะภายนอกให้กับแต่ละรายการเหมือนการกำหนด margin แยกทุกชิ้น สามารถกำหนดระยะเดียวกันทุกทิศทาง หรือใช้ `row-gap` และ `column-gap` เพื่อแยกระยะตามแนวตั้งและแนวนอน
.gallery {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
row-gap: 24px;
column-gap: 16px;
}
/* เขียนแบบย่อได้เช่นกัน */
.gallery-compact {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px 24px;
}
ในรูปแบบย่อ `gap: 16px 24px` หมายถึงระยะระหว่างแถว 16 พิกเซล และระยะระหว่างคอลัมน์ 24 พิกเซล การใช้ gap ทำให้จัดการช่องว่างได้เป็นระบบ โดยเฉพาะหน้าที่มีการ์ดจำนวนมาก
5. จัดวางแถวและคอลัมน์ด้วย Grid Placement
Grid สามารถกำหนดตำแหน่งของรายการแต่ละชิ้นให้กินพื้นที่มากกว่าหนึ่งแถวหรือหนึ่งคอลัมน์ได้ เช่น `grid-column: span 2` ให้รายการขยายครอบคลุมสองคอลัมน์ หรือกำหนดเส้นเริ่มต้นและเส้นสิ้นสุดด้วย `grid-column: 1 / 3`
.dashboard {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
.dashboard-header {
grid-column: 1 / -1;
}
.dashboard-feature {
grid-column: span 2;
}
ค่า `grid-column: 1 / -1` ทำให้รายการเริ่มจากเส้นคอลัมน์แรกไปจนถึงเส้นสุดท้าย จึงครอบคลุมทุกคอลัมน์ที่มีอยู่ใน Grid เหมาะกับหัวข้อหรือแถบสรุปที่ต้องการเต็มความกว้าง ส่วน `span 2` ใช้ขยายรายการให้ครอบคลุมสองคอลัมน์
6. ใช้ grid-template-areas สร้างโครงหน้าเว็บ
หากต้องจัด Layout ที่มีส่วนหัว แถบเมนู เนื้อหาหลัก และส่วนท้าย `grid-template-areas` ช่วยให้มองเห็นโครงสร้างหน้าเว็บได้ชัดเจน โดยกำหนดชื่อพื้นที่ให้กับแต่ละช่อง จากนั้นกำหนดชื่อเดียวกันให้กับองค์ประกอบลูกผ่าน `grid-area`
.site-layout {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.site-header { grid-area: header; }
.site-sidebar { grid-area: sidebar; }
.site-main { grid-area: main; }
.site-footer { grid-area: footer; }
การเขียนชื่อพื้นที่เป็นข้อความช่วยให้เข้าใจตำแหน่งของส่วนต่าง ๆ ได้ง่าย โดยแต่ละแถวของ `grid-template-areas` ต้องมีจำนวนช่องสอดคล้องกับจำนวนคอลัมน์ หากต้องการพื้นที่ว่างในแผนผังสามารถใช้จุด (`.`) แทนช่องที่ไม่มีชื่อพื้นที่ได้
7. Responsive Grid สำหรับมือถือและแท็บเล็ต
เว็บไซต์ควรปรับรูปแบบตามขนาดหน้าจอ เพื่อให้เนื้อหาอ่านง่ายและไม่เกิดการเลื่อนในแนวนอนโดยไม่จำเป็น วิธีพื้นฐานคือใช้ Media Query เปลี่ยนจำนวนคอลัมน์เมื่อหน้าจอแคบลง อีกทางเลือกหนึ่งคือใช้ `auto-fit` ร่วมกับ `minmax()` เพื่อให้ Grid จัดจำนวนคอลัมน์ตามพื้นที่ที่มีอยู่
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
}
@media (max-width: 520px) {
.cards {
grid-template-columns: 1fr;
gap: 14px;
}
}
`auto-fit` จะจัดคอลัมน์ให้พอดีกับพื้นที่ที่มีและขยายแทร็กที่เหลือให้ใช้พื้นที่ได้เต็มขึ้น ส่วน `auto-fill` จะพยายามรักษาแทร็กที่สามารถสร้างได้ แม้บางแทร็กจะไม่มีรายการอยู่ก็ตาม เลือกใช้ตามผลลัพธ์ที่ต้องการ และทดสอบกับความกว้างหน้าจอจริงเสมอ
| ขนาดหน้าจอโดยประมาณ | แนวทาง Layout | เป้าหมาย |
|---|---|---|
| มากกว่า 1000px | 3–4 คอลัมน์ | ใช้พื้นที่หน้าจอขนาดใหญ่ได้คุ้มค่า |
| 700–1000px | 2–3 คอลัมน์ | รักษาความอ่านง่ายและพื้นที่การ์ด |
| ต่ำกว่า 700px | 1–2 คอลัมน์ | ลดความหนาแน่นของเนื้อหา |
| ต่ำกว่า 480px | 1 คอลัมน์ในหลายกรณี | ให้ข้อความและปุ่มมีพื้นที่เพียงพอ |
อย่ายึดติดกับตัวเลขเพียงอย่างเดียวค่าความกว้างในตัวอย่างเป็นจุดเริ่มต้น ไม่ใช่มาตรฐานตายตัว ควรเลือก Breakpoint จากจุดที่เนื้อหาเริ่มแน่น การ์ดแคบเกินไป หรือการนำทางใช้งานยาก
8. ตัวอย่างหน้า Product Grid แบบสมบูรณ์
ตัวอย่างต่อไปเป็นหน้าแสดงรายการสินค้าที่ใช้ CSS Grid จัดการ์ดให้เปลี่ยนจำนวนคอลัมน์ตามพื้นที่หน้าจอ มีส่วนหัว ช่องค้นหา ป้ายสถานะ และปุ่มกรองตัวอย่าง ปุ่มกรองสามารถคลิกเพื่อเลือกประเภทสินค้า และช่องค้นหาจะกรองรายการจากชื่อและคำอธิบายได้โดยใช้ JavaScript ภายในหน้าเดียว
ขอบเขตของตัวอย่างข้อมูลสินค้าในหน้านี้เป็นข้อมูลตัวอย่างที่เขียนไว้ใน HTML ไม่มีระบบชำระเงิน ไม่มีการส่งคำสั่งซื้อ และไม่มีการเชื่อมต่อฐานข้อมูล
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Grid Product Gallery</title>
<style>
* {
box-sizing: border-box;
}
:root {
color-scheme: light;
font-family: Tahoma, Arial, sans-serif;
color: #172033;
background: #f1f5f9;
}
body {
margin: 0;
line-height: 1.6;
}
button,
input {
font: inherit;
}
.page {
width: min(1120px, 100% - 32px);
margin: 0 auto;
padding: 30px 0 44px;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 24px;
}
.brand {
margin: 0;
color: #1d4ed8;
font-size: 1.35rem;
}
.top-label {
padding: 6px 12px;
color: #475569;
background: #e2e8f0;
border-radius: 999px;
font-size: 0.85rem;
}
.hero {
margin-bottom: 26px;
padding: 30px;
color: #ffffff;
background: linear-gradient(135deg, #1d4ed8, #6d28d9);
border-radius: 20px;
}
.hero h1 {
margin: 0 0 10px;
font-size: clamp(1.8rem, 4vw, 2.7rem);
line-height: 1.25;
}
.hero p {
max-width: 640px;
margin: 0;
color: #e0e7ff;
}
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.search {
flex: 1 1 220px;
min-width: 0;
padding: 11px 14px;
color: #172033;
background: #ffffff;
border: 1px solid #cbd5e1;
border-radius: 10px;
}
.filter {
padding: 10px 14px;
color: #334155;
background: #ffffff;
border: 1px solid #cbd5e1;
border-radius: 10px;
cursor: pointer;
}
.filter[aria-pressed="true"] {
color: #ffffff;
background: #1d4ed8;
border-color: #1d4ed8;
}
.results-label {
margin: 0 0 14px;
color: #64748b;
font-size: 0.92rem;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
gap: 18px;
}
.product-card {
display: flex;
flex-direction: column;
min-width: 0;
padding: 20px;
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 16px;
box-shadow: 0 5px 18px rgba(15, 23, 42, 0.05);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.product-card:hover {
transform: translateY(-3px);
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.09);
}
.product-icon {
display: grid;
place-items: center;
min-height: 116px;
margin-bottom: 16px;
background: #eff6ff;
border-radius: 12px;
font-size: 2.7rem;
}
.product-card h2 {
margin: 0 0 8px;
font-size: 1.1rem;
}
.product-card p {
flex: 1;
margin: 0 0 16px;
color: #64748b;
font-size: 0.94rem;
}
.category {
align-self: flex-start;
margin-bottom: 12px;
padding: 4px 9px;
color: #1d4ed8;
background: #dbeafe;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 700;
}
.details {
padding: 10px 14px;
color: #ffffff;
background: #2563eb;
border: 0;
border-radius: 9px;
cursor: pointer;
}
.details:hover {
background: #1e40af;
}
.empty {
grid-column: 1 / -1;
padding: 28px;
color: #64748b;
background: #ffffff;
border: 1px dashed #cbd5e1;
border-radius: 12px;
text-align: center;
}
.footer {
margin-top: 28px;
padding-top: 18px;
color: #64748b;
border-top: 1px solid #cbd5e1;
font-size: 0.9rem;
}
@media (max-width: 520px) {
.page {
width: min(100% - 24px, 1120px);
padding-top: 18px;
}
.hero {
padding: 22px;
}
.topbar {
align-items: flex-start;
flex-direction: column;
}
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<main class="page">
<header class="topbar">
<p class="brand"><strong>GridMarket</strong></p>
<span class="top-label">CSS Grid Learning Demo</span>
</header>
<section class="hero">
<h1>สำรวจสินค้าในรูปแบบ Grid</h1>
<p>ตัวอย่างการใช้ CSS Grid สร้างการ์ดหลายคอลัมน์ที่ปรับตามหน้าจอ พร้อมค้นหาและกรองรายการสินค้า</p>
</section>
<section aria-label="ค้นหาและกรองสินค้า">
<div class="toolbar">
<input class="search" id="search" type="search" placeholder="ค้นหาสินค้า..." aria-label="ค้นหาสินค้า">
<button class="filter" type="button" data-filter="all" aria-pressed="true">ทั้งหมด</button>
<button class="filter" type="button" data-filter="design" aria-pressed="false">งานออกแบบ</button>
<button class="filter" type="button" data-filter="dev" aria-pressed="false">พัฒนาเว็บ</button>
</div>
<p class="results-label" id="results" aria-live="polite">แสดงสินค้า 6 รายการ</p>
</section>
<section class="product-grid" id="productGrid" aria-label="รายการสินค้า">
<article class="product-card" data-category="design" data-search="ชุดไอคอนออกแบบกราฟิก design icons">
<div class="product-icon" aria-hidden="true">🎨</div>
<span class="category">งานออกแบบ</span>
<h2>ชุดไอคอนสร้างสรรค์</h2>
<p>ตัวอย่างทรัพยากรสำหรับใช้ประกอบงานออกแบบส่วนติดต่อผู้ใช้</p>
<button class="details" type="button" data-name="ชุดไอคอนสร้างสรรค์">ดูรายละเอียด</button>
</article>
<article class="product-card" data-category="dev" data-search="starter kit html css frontend website">
<div class="product-icon" aria-hidden="true">💻</div>
<span class="category">พัฒนาเว็บ</span>
<h2>Frontend Starter Kit</h2>
<p>โครงสร้างตัวอย่างสำหรับเริ่มสร้างหน้าเว็บด้วย HTML และ CSS</p>
<button class="details" type="button" data-name="Frontend Starter Kit">ดูรายละเอียด</button>
</article>
<article class="product-card" data-category="design" data-search="ชุดสี ui palette color design">
<div class="product-icon" aria-hidden="true">🌈</div>
<span class="category">งานออกแบบ</span>
<h2>UI Color Palette</h2>
<p>ชุดสีตัวอย่างสำหรับสร้างธีมที่สม่ำเสมอและอ่านง่าย</p>
<button class="details" type="button" data-name="UI Color Palette">ดูรายละเอียด</button>
</article>
<article class="product-card" data-category="dev" data-search="responsive layout grid css web">
<div class="product-icon" aria-hidden="true">📐</div>
<span class="category">พัฒนาเว็บ</span>
<h2>Responsive Layout Lab</h2>
<p>ตัวอย่างทดลองปรับ Layout ให้เหมาะกับหลายขนาดหน้าจอ</p>
<button class="details" type="button" data-name="Responsive Layout Lab">ดูรายละเอียด</button>
</article>
<article class="product-card" data-category="design" data-search="dashboard wireframe design prototype">
<div class="product-icon" aria-hidden="true">🧩</div>
<span class="category">งานออกแบบ</span>
<h2>Dashboard Wireframe</h2>
<p>โครงร่างตัวอย่างสำหรับวางแผงข้อมูลและองค์ประกอบบน Dashboard</p>
<button class="details" type="button" data-name="Dashboard Wireframe">ดูรายละเอียด</button>
</article>
<article class="product-card" data-category="dev" data-search="accessibility web components frontend">
<div class="product-icon" aria-hidden="true">♿</div>
<span class="category">พัฒนาเว็บ</span>
<h2>Accessible Components</h2>
<p>ตัวอย่างองค์ประกอบที่คำนึงถึงการเข้าถึงและการใช้งานด้วยคีย์บอร์ด</p>
<button class="details" type="button" data-name="Accessible Components">ดูรายละเอียด</button>
</article>
</section>
<footer class="footer">
ตัวอย่างเพื่อการเรียนรู้ CSS Grid เท่านั้น ไม่มีการสั่งซื้อหรือชำระเงินจริง
</footer>
</main>
<script>
const searchInput = document.getElementById('search');
const filterButtons = Array.from(document.querySelectorAll('[data-filter]'));
const cards = Array.from(document.querySelectorAll('.product-card'));
const grid = document.getElementById('productGrid');
const results = document.getElementById('results');
let activeFilter = 'all';
function updateProducts() {
const query = searchInput.value.trim().toLocaleLowerCase();
let visibleCount = 0;
cards.forEach((card) => {
const categoryMatches = activeFilter === 'all' || card.dataset.category === activeFilter;
const textMatches = card.dataset.search.toLocaleLowerCase().includes(query);
const visible = categoryMatches && textMatches;
card.hidden = !visible;
if (visible) visibleCount += 1;
});
let emptyMessage = document.getElementById('emptyMessage');
if (visibleCount === 0) {
if (!emptyMessage) {
emptyMessage = document.createElement('p');
emptyMessage.id = 'emptyMessage';
emptyMessage.className = 'empty';
emptyMessage.textContent = 'ไม่พบสินค้าที่ตรงกับเงื่อนไข ลองเปลี่ยนคำค้นหาหรือหมวดหมู่';
grid.appendChild(emptyMessage);
}
} else if (emptyMessage) {
emptyMessage.remove();
}
results.textContent = `แสดงสินค้า ${visibleCount} รายการ`;
}
filterButtons.forEach((button) => {
button.addEventListener('click', () => {
activeFilter = button.dataset.filter;
filterButtons.forEach((item) => {
item.setAttribute('aria-pressed', String(item === button));
});
updateProducts();
});
});
searchInput.addEventListener('input', updateProducts);
document.querySelectorAll('.details').forEach((button) => {
button.addEventListener('click', () => {
window.alert(`นี่คือข้อมูลตัวอย่างของ ${button.dataset.name}`);
});
});
</script>
</body>
</html>
9. ปัญหาที่พบบ่อยเมื่อใช้ CSS Grid
ทำไม Grid ไม่แสดงหลายคอลัมน์ตามที่กำหนด
ตรวจสอบว่าได้กำหนด display: grid ให้กับองค์ประกอบแม่หรือไม่ ตรวจสอบชื่อคลาสใน HTML และ CSS ให้ตรงกัน รวมถึงดูว่ามีกฎ Media Query หรือกฎ CSS อื่นเขียนทับ grid-template-columns อยู่หรือไม่
ทำไมการ์ดบางใบทำให้หน้าเว็บล้นในแนวนอน
ตรวจสอบเนื้อหาที่ยาวมาก รูปภาพที่ไม่มี max-width: 100% และขนาดขั้นต่ำของคอลัมน์ การใช้ minmax(0, 1fr) และ min-width: 0 กับ Grid Item สามารถช่วยลดปัญหาเนื้อหาดันคอลัมน์ให้กว้างเกินพื้นที่ได้ในหลายกรณี
ควรใช้ Grid หรือ Flexbox กับการ์ดสินค้า
ทั้งสองแบบใช้งานได้ หากต้องการโครงสร้างหลายแถวและหลายคอลัมน์ที่จัดแนวเป็นระบบ Grid มักเหมาะกว่า หากต้องการจัดรายการตามแนวเดียวและให้รายการห่อบรรทัดได้ Flexbox อาจเหมาะสมกว่า สามารถใช้ Grid จัดกลุ่มการ์ดและ Flexbox จัดเนื้อหาภายในการ์ดได้เช่นกัน
auto-fit กับ auto-fill ต่างกันอย่างไร
ทั้งสองช่วยสร้างจำนวนแทร็กตามพื้นที่ที่มีร่วมกับ repeat() และ minmax() โดย auto-fit จะยุบแทร็กว่างที่สร้างได้เพื่อให้แทร็กที่มีรายการขยายพื้นที่ ส่วน auto-fill จะคงแทร็กที่สามารถสร้างได้ไว้ แม้บางแทร็กไม่มีรายการ จึงอาจทำให้การกระจายพื้นที่ต่างกัน
10. แบบฝึกหัดท้ายบท
ลองปรับโค้ดในตัวอย่างเพื่อฝึกด้วยตนเอง: (1) เปลี่ยนจำนวนคอลัมน์เป็นสองคอลัมน์บนหน้าจอขนาดกลาง (2) เพิ่มช่องว่างระหว่างการ์ดเป็น 24 พิกเซล (3) ทำให้การ์ดแรกกินพื้นที่สองคอลัมน์บนหน้าจอขนาดใหญ่ แต่กลับมาเรียงหนึ่งคอลัมน์เมื่อเปิดบนมือถือ และ (4) เปลี่ยนสีพื้นหลังของการ์ดให้แตกต่างกันตามหมวดหมู่
แนวทางตรวจคำตอบใช้ `grid-template-columns` เพื่อกำหนดจำนวนคอลัมน์ ใช้ `gap` เพื่อควบคุมระยะห่าง ใช้ `grid-column: span 2` สำหรับการ์ดที่ต้องการขยาย และใช้ Media Query เพื่อปรับตำแหน่งหรือจำนวนคอลัมน์เมื่อพื้นที่หน้าจอเปลี่ยนไป
11. สรุปบทเรียน
CSS Grid ช่วยสร้างโครงสร้างหน้าเว็บที่ยืดหยุ่นและควบคุมได้ทั้งแถวและคอลัมน์ คุณสมบัติสำคัญที่ควรจดจำ ได้แก่ `display: grid`, `grid-template-columns`, `grid-template-rows`, `fr`, `repeat()`, `minmax()`, `gap`, `grid-column`, `grid-row` และ `grid-template-areas` เมื่อใช้ร่วมกับ Media Query จะสามารถสร้างหน้าเว็บที่เหมาะกับคอมพิวเตอร์ แท็บเล็ต และโทรศัพท์ได้อย่างเป็นระบบ
Layout ที่ดีไม่ใช่เพียงการวางองค์ประกอบให้ครบ แต่คือการทำให้ข้อมูลมีลำดับชัดเจนและใช้งานได้ดีในทุกขนาดหน้าจอ
ภารกิจต่อไป: นำ CSS Grid ไปใช้จัดหน้า Dashboard หรือหน้ารวมสินค้าอย่างน้อยหนึ่งหน้า โดยทดสอบที่ความกว้าง 1280px, 768px และ 375px ตรวจสอบว่าการ์ดไม่ล้น เนื้อหาไม่ถูกตัด และผู้ใช้ยังสามารถอ่านและใช้งานองค์ประกอบสำคัญได้สะดวก
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง
⏳ กำลังจับเวลาอ่าน...