Guest Adventurer 🥉 Lv.1
🪙 50
🔷 10
← Back to library
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เป้าหมาย
มากกว่า 1000px3–4 คอลัมน์ใช้พื้นที่หน้าจอขนาดใหญ่ได้คุ้มค่า
700–1000px2–3 คอลัมน์รักษาความอ่านง่ายและพื้นที่การ์ด
ต่ำกว่า 700px1–2 คอลัมน์ลดความหนาแน่นของเนื้อหา
ต่ำกว่า 480px1 คอลัมน์ในหลายกรณีให้ข้อความและปุ่มมีพื้นที่เพียงพอ
อย่ายึดติดกับตัวเลขเพียงอย่างเดียวค่าความกว้างในตัวอย่างเป็นจุดเริ่มต้น ไม่ใช่มาตรฐานตายตัว ควรเลือก 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 ตรวจสอบว่าการ์ดไม่ล้น เนื้อหาไม่ถูกตัด และผู้ใช้ยังสามารถอ่านและใช้งานองค์ประกอบสำคัญได้สะดวก
Reach within 500px of the page end and stay for at least 5 minutes to earn EXP once per day.

⏳ Reading timer is running...

🌐
Select Language
21 World Languages Supported