Article · programming
HTML & CSS EP10: CSS Grid — สร้างเลย์เอาต์เว็บไซต์หลายคอลัมน์อย่างมืออาชีพ
HTML & CSS
บทเรียน HTML และ CSS ตอนที่ 10 เรียนรู้ CSS Grid ตั้งแต่แนวคิดพื้นฐาน การกำหนดแถวและคอลัมน์ การจัดวางพื้นที่ การใช้ repeat(), minmax(), auto-fit, auto-fill และการสร้างหน้าเว็บแบบ Responsive พร้อมตัวอย่าง HTML ที่นำไปทดลองใช้งานได้ทันที
HTML & CSS EP10: CSS Grid คืออะไร และช่วยจัดหน้าเว็บไซต์อย่างไร
เมื่อเว็บไซต์มีองค์ประกอบจำนวนมาก เช่น แถบเมนู การ์ดสินค้า รูปภาพ บทความ และแผงข้อมูล การจัดวางด้วยการกำหนดความกว้างและระยะห่างทีละส่วนอาจทำให้โค้ดซับซ้อนขึ้น CSS Grid เป็นระบบจัดเลย์เอาต์สองมิติที่ช่วยจัดองค์ประกอบตามแถวและคอลัมน์ได้อย่างเป็นระบบ เหมาะกับหน้าแดชบอร์ด แกลเลอรีสินค้า หน้าข่าว และหน้า Landing Page ที่ต้องการควบคุมตำแหน่งของเนื้อหาอย่างยืดหยุ่น
💡 แนวคิดสำคัญFlexbox เหมาะกับการจัดองค์ประกอบเป็นแนวหลักหนึ่งมิติ เช่น แถวปุ่มหรือแถบเมนู ส่วน CSS Grid เหมาะกับเลย์เอาต์ที่ต้องควบคุมทั้งแถวและคอลัมน์ ทั้งสองระบบสามารถใช้ร่วมกันในหน้าเดียวได้
1. เข้าใจ Grid Container และ Grid Items
CSS Grid มีองค์ประกอบหลักสองส่วน ได้แก่ **Grid Container** ซึ่งเป็นกล่องแม่ที่กำหนดระบบตาราง และ **Grid Items** ซึ่งเป็นองค์ประกอบลูกโดยตรงภายในกล่องนั้น เมื่อตั้งค่า `display: grid` ให้กับกล่องแม่ ลูกโดยตรงจะกลายเป็นรายการใน Grid โดยอัตโนมัติ ส่วนองค์ประกอบที่อยู่ลึกลงไปภายในลูกจะไม่ได้กลายเป็น Grid Item ของกล่องแม่โดยตรง
<div class="grid-container">
<div class="grid-item">รายการ 1</div>
<div class="grid-item">รายการ 2</div>
<div class="grid-item">รายการ 3</div>
</div>
<style>
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
}
.grid-item {
padding: 24px;
background: #dbeafe;
border-radius: 8px;
text-align: center;
}
</style>
ในตัวอย่างนี้ `display: grid` เปิดใช้ระบบ Grid และ `grid-template-columns: 1fr 1fr 1fr` สร้างคอลัมน์สามคอลัมน์ที่แบ่งพื้นที่ว่างเท่า ๆ กัน ส่วน `gap` กำหนดช่องว่างระหว่างรายการโดยไม่ต้องเพิ่ม margin ให้แต่ละกล่อง
2. กำหนดคอลัมน์ด้วย grid-template-columns
คุณสมบัติ `grid-template-columns` ใช้กำหนดจำนวนและขนาดคอลัมน์ ตัวอย่างเช่น `200px 1fr` หมายถึงคอลัมน์แรกกว้าง 200 พิกเซล และคอลัมน์ที่สองรับพื้นที่ว่างที่เหลือ ส่วน `1fr 2fr` แบ่งพื้นที่ว่างเป็นสัดส่วนหนึ่งต่อสอง ไม่ใช่การกำหนดความกว้างแบบตายตัว
| รูปแบบ | ความหมาย | ตัวอย่างการใช้งาน |
|---|---|---|
| 200px 200px | สองคอลัมน์ที่กำหนดความกว้างตายตัว | กล่องขนาดคงที่ |
| 1fr 1fr | แบ่งพื้นที่ว่างเท่ากันสองส่วน | เลย์เอาต์สองคอลัมน์ |
| 1fr 2fr | แบ่งพื้นที่ว่างเป็นสัดส่วนหนึ่งต่อสอง | เนื้อหาหลักและแผงข้าง |
| repeat(3, 1fr) | สร้างสามคอลัมน์ขนาดเท่ากัน | ตารางการ์ด |
| repeat(4, minmax(0, 1fr)) | สร้างสี่คอลัมน์ โดยแต่ละคอลัมน์มีขนาดยืดหยุ่นและยอมให้หดได้ | แดชบอร์ดหรือการ์ดข้อมูล |
2.1 ใช้ repeat() ลดการเขียน CSS ซ้ำ
เมื่อคอลัมน์มีขนาดซ้ำกัน สามารถใช้ `repeat()` เพื่อทำให้โค้ดสั้นและอ่านง่ายขึ้น เช่น `repeat(3, 1fr)` มีความหมายเทียบเท่ากับ `1fr 1fr 1fr` เหมาะกับเลย์เอาต์ที่มีการ์ดหลายรายการ และช่วยให้แก้จำนวนคอลัมน์ได้ง่ายในภายหลัง
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
3. จัดการแถวด้วย grid-template-rows
หากต้องการกำหนดความสูงหรือสัดส่วนของแถว สามารถใช้ `grid-template-rows` เช่น `80px auto` เพื่อกำหนดแถวแรกให้สูง 80 พิกเซล และให้แถวที่สองปรับตามเนื้อหาในกรณีทั่วไป นอกจากนี้ยังใช้หน่วย `fr` เพื่อแบ่งพื้นที่ว่างในแกนแถวได้เมื่อความสูงของ Grid Container ถูกกำหนดไว้อย่างเหมาะสม
.layout {
display: grid;
grid-template-rows: 80px 1fr auto;
min-height: 100vh;
}
.header { grid-row: 1; }
.main { grid-row: 2; }
.footer { grid-row: 3; }
⚠️ ข้อควรระวังเกี่ยวกับ frหน่วย `fr` ใช้แบ่งพื้นที่ว่างใน Grid ไม่ได้หมายความว่าเป็นเปอร์เซ็นต์ของพื้นที่ทั้งหมดโดยตรง หากต้องการให้แถวแบ่งความสูงตามสัดส่วน ควรกำหนดความสูงของ Grid Container ให้ชัดเจน และตรวจสอบผลเมื่อเนื้อหามีความสูงแตกต่างกัน
4. กำหนดช่องว่างด้วย gap
คุณสมบัติ `gap` กำหนดระยะห่างระหว่างแถวและคอลัมน์ได้ในคำสั่งเดียว เช่น `gap: 16px` ใช้ระยะห่าง 16 พิกเซลทั้งสองทิศทาง หากต้องการกำหนดแยกกัน ให้ใช้ `row-gap` สำหรับระยะระหว่างแถว และ `column-gap` สำหรับระยะระหว่างคอลัมน์
.gallery {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
row-gap: 24px;
column-gap: 16px;
}
5. จัดวางรายการให้กินพื้นที่หลายแถวหรือหลายคอลัมน์
คุณสมบัติ `grid-column` และ `grid-row` ใช้กำหนดช่วงเส้น Grid ที่รายการจะครอบครอง ตัวอย่าง `grid-column: span 2` ทำให้รายการกินพื้นที่สองคอลัมน์ และ `grid-row: span 2` ทำให้รายการกินพื้นที่สองแถว เหมาะกับการ์ดเด่นในหน้าแรกหรือภาพขนาดใหญ่ในแกลเลอรี
.featured-card {
grid-column: span 2;
grid-row: span 2;
}
/* กำหนดตำแหน่งระหว่างเส้น Grid โดยตรง */
.sidebar {
grid-column: 1;
grid-row: 1 / 3;
}
การใช้ `span` ไม่ได้กำหนดว่ารายการต้องเริ่มจากคอลัมน์ใด แต่ระบุว่ารายการนั้นควรกินพื้นที่กี่แทร็ก ส่วนการกำหนดค่าเช่น `grid-column: 1 / 3` หมายถึงเริ่มจากเส้น Grid หมายเลข 1 ไปสิ้นสุดที่เส้นหมายเลข 3 ซึ่งครอบคลุมพื้นที่สองคอลัมน์
6. ตั้งชื่อพื้นที่ด้วย grid-template-areas
สำหรับหน้าเว็บที่มีส่วนประกอบชัดเจน เช่น Header, Sidebar, Main และ Footer สามารถใช้ `grid-template-areas` เพื่ออธิบายโครงสร้างเลย์เอาต์ด้วยชื่อแทนหมายเลขเส้น วิธีนี้ช่วยให้ CSS อ่านง่ายและเห็นภาพรวมของหน้าได้รวดเร็ว
.page-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
🧩 เคล็ดลับการตั้งชื่อพื้นที่แต่ละแถวใน `grid-template-areas` ต้องมีจำนวนเซลล์สอดคล้องกับโครงสร้างคอลัมน์ หากต้องการเว้นพื้นที่ว่างในแผนผัง สามารถใช้เครื่องหมายจุด (`.`) แทนเซลล์ที่ไม่มีรายการได้
7. ทำเลย์เอาต์ Responsive ด้วย minmax(), auto-fit และ auto-fill
เว็บไซต์ควรแสดงผลได้ดีบนหน้าจอหลายขนาด การกำหนดคอลัมน์จำนวนคงที่อาจทำให้การ์ดแคบเกินไปบนมือถือ วิธีหนึ่งคือใช้ `repeat(auto-fit, minmax(220px, 1fr))` เพื่อให้เบราว์เซอร์สร้างจำนวนคอลัมน์ที่เหมาะกับพื้นที่ที่มี โดยคอลัมน์จะมีขนาดขั้นต่ำประมาณ 220 พิกเซลและขยายได้ตามพื้นที่ว่าง
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
}
`auto-fit` และ `auto-fill` มีพฤติกรรมใกล้เคียงกัน แต่ต่างกันเมื่อมีพื้นที่สำหรับคอลัมน์ว่าง `auto-fill` พยายามคงแทร็กที่สร้างได้ไว้ แม้ไม่มีรายการมาวาง ส่วน `auto-fit` จะยุบแทร็กว่างเพื่อให้รายการที่มีอยู่สามารถขยายพื้นที่ได้ ทั้งนี้ผลลัพธ์ขึ้นอยู่กับขนาดพื้นที่และจำนวนรายการจริง
| คุณสมบัติ | หน้าที่ | ตัวอย่าง |
|---|---|---|
| minmax() | กำหนดขนาดขั้นต่ำและขั้นสูงของแทร็ก | minmax(220px, 1fr) |
| auto-fit | ปรับคอลัมน์ให้เต็มพื้นที่ โดยยุบแทร็กว่างเมื่อเหมาะสม | repeat(auto-fit, minmax(220px, 1fr)) |
| auto-fill | สร้างแทร็กตามพื้นที่ แม้บางแทร็กอาจไม่มีรายการ | repeat(auto-fill, minmax(220px, 1fr)) |
| minmax(0, 1fr) | ช่วยให้คอลัมน์หดได้และลดปัญหาเนื้อหาดันเลย์เอาต์กว้างเกิน | repeat(3, minmax(0, 1fr)) |
8. จัดแนวรายการภายใน Grid
CSS Grid มีคุณสมบัติสำหรับจัดแนวทั้งรายการและเนื้อหาภายในพื้นที่ เช่น `justify-items` สำหรับแนวแกนแถว และ `align-items` สำหรับแนวแกนคอลัมน์ในระบบการจัดแนวของ Grid ส่วน `place-items` เป็นคำสั่งย่อที่รวม `align-items` และ `justify-items` เข้าด้วยกัน สำหรับการจัดแนวแทร็กทั้งชุด สามารถใช้ `justify-content` และ `align-content` ได้เมื่อมีพื้นที่ว่างใน Grid Container
.grid-container {
display: grid;
place-items: center;
gap: 16px;
}
/* จัดตำแหน่งรายการแต่ละชิ้น */
.grid-container {
justify-items: stretch;
align-items: start;
}
ควรแยกให้ออกระหว่างการจัดแนวรายการภายในเซลล์กับการจัดตำแหน่งของ Grid ทั้งชุด หากไม่เห็นความเปลี่ยนแปลงจาก `justify-content` หรือ `align-content` ให้ตรวจสอบว่าคอนเทนเนอร์มีพื้นที่ว่างในทิศทางนั้นเพียงพอหรือไม่
9. ตัวอย่างการใช้งานจริง: สร้าง Product Grid
ตัวอย่างต่อไปนี้จำลองพื้นที่แสดงสินค้าแบบการ์ด แต่ละการ์ดประกอบด้วยชื่อ รายละเอียด ราคา และปุ่มเลือกดูสินค้า โดยใช้ CSS Grid เพื่อปรับจำนวนคอลัมน์อัตโนมัติตามขนาดพื้นที่ และใช้ Flexbox ภายในตัวการ์ดเพื่อให้ปุ่มอยู่ด้านล่างอย่างสม่ำเสมอ
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Grid Example</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
color: #172033;
background: #f1f5f9;
}
h1 { margin-top: 0; }
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 18px;
max-width: 1100px;
margin: 0 auto;
}
.product-card {
display: flex;
flex-direction: column;
padding: 20px;
border: 1px solid #dbe3ef;
border-radius: 16px;
background: #fff;
box-shadow: 0 6px 18px rgba(15, 23, 42, .06);
}
.product-icon {
display: grid;
place-items: center;
min-height: 110px;
border-radius: 12px;
background: #dbeafe;
font-size: 42px;
}
.product-card h2 { font-size: 1.15rem; }
.product-card p { color: #526078; line-height: 1.6; }
.price { margin-top: auto; font-weight: 800; font-size: 1.25rem; }
button {
width: 100%;
margin-top: 14px;
padding: 11px 14px;
border: 0;
border-radius: 9px;
background: #2563eb;
color: #fff;
font: inherit;
font-weight: 700;
cursor: pointer;
}
button:hover { background: #1d4ed8; }
button:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; }
.notice {
max-width: 1100px;
margin: 20px auto 0;
padding: 14px;
border-radius: 10px;
background: #dcfce7;
color: #166534;
}
</style>
</head>
<body>
<main>
<h1>สินค้าแนะนำ</h1>
<p>ตัวอย่างเลย์เอาต์ที่ปรับจำนวนคอลัมน์ตามพื้นที่หน้าจอ</p>
<section class="product-grid" aria-label="รายการสินค้า">
<article class="product-card">
<div class="product-icon" aria-hidden="true">💻</div>
<h2>โน้ตบุ๊กสำหรับทำงาน</h2>
<p>เหมาะสำหรับงานเอกสาร การเรียน และการพัฒนาเว็บไซต์</p>
<div class="price">฿18,900</div>
<button type="button" data-product="โน้ตบุ๊กสำหรับทำงาน">ดูสินค้า</button>
</article>
<article class="product-card">
<div class="product-icon" aria-hidden="true">⌨️</div>
<h2>คีย์บอร์ดไร้สาย</h2>
<p>ช่วยให้โต๊ะทำงานเป็นระเบียบและใช้งานสะดวกขึ้น</p>
<div class="price">฿1,290</div>
<button type="button" data-product="คีย์บอร์ดไร้สาย">ดูสินค้า</button>
</article>
<article class="product-card">
<div class="product-icon" aria-hidden="true">🖱️</div>
<h2>เมาส์สำหรับทำงาน</h2>
<p>ออกแบบให้เหมาะกับการใช้งานทั่วไปในชีวิตประจำวัน</p>
<div class="price">฿790</div>
<button type="button" data-product="เมาส์สำหรับทำงาน">ดูสินค้า</button>
</article>
<article class="product-card">
<div class="product-icon" aria-hidden="true">🎧</div>
<h2>หูฟังสำหรับประชุม</h2>
<p>เหมาะกับการประชุมออนไลน์และการเรียนผ่านวิดีโอ</p>
<div class="price">฿1,590</div>
<button type="button" data-product="หูฟังสำหรับประชุม">ดูสินค้า</button>
</article>
</section>
<p class="notice" id="notice" role="status" aria-live="polite">เลือกดูสินค้าเพื่อทดลองการทำงานของปุ่ม</p>
</main>
<script>
const notice = document.getElementById('notice');
document.querySelectorAll('[data-product]').forEach((button) => {
button.addEventListener('click', () => {
notice.textContent = 'คุณเลือกดู: ' + button.dataset.product + ' — นี่เป็นตัวอย่างการทำงานภายในหน้าเว็บเท่านั้น';
});
});
</script>
</body>
</html>
10. สร้าง Dashboard ด้วย Grid Areas
หน้าแดชบอร์ดมักประกอบด้วยแถบด้านบน เมนูด้านข้าง พื้นที่เนื้อหาหลัก และแผงสรุปข้อมูล CSS Grid ช่วยให้ระบุพื้นที่ของแต่ละส่วนได้อย่างชัดเจน และสามารถเปลี่ยนผังเมื่อหน้าจอเล็กลงได้โดยไม่ต้องเปลี่ยนโครงสร้าง HTML ทั้งหมด
.dashboard {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
grid-template-areas:
"sidebar header"
"sidebar content";
gap: 16px;
}
.dashboard-header { grid-area: header; }
.dashboard-sidebar { grid-area: sidebar; }
.dashboard-content { grid-area: content; }
@media (max-width: 700px) {
.dashboard {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"content"
"sidebar";
}
}
11. ปัญหาที่พบบ่อยและวิธีแก้ไข
ทำไมการ์ดจึงดันหน้าเว็บให้กว้างเกินหน้าจอ?
สาเหตุหนึ่งคือเนื้อหาภายในรายการมีขนาดขั้นต่ำโดยปริยาย เช่น ข้อความยาวหรือรูปภาพขนาดใหญ่ ลองใช้ `minmax(0, 1fr)` กับคอลัมน์ กำหนด `min-width: 0` ให้รายการที่จำเป็น และใช้ `max-width: 100%` กับรูปภาพ พร้อมตรวจสอบว่าไม่มีข้อความหรือองค์ประกอบที่กำหนดความกว้างตายตัวเกินพื้นที่
ควรใช้ Grid หรือ Flexbox เมื่อใด?
เลือก Grid เมื่อเลย์เอาต์ต้องควบคุมทั้งแถวและคอลัมน์ เช่น ตารางการ์ดหรือหน้าแดชบอร์ด เลือก Flexbox เมื่อจัดองค์ประกอบตามแกนหลักเดียว เช่น เมนูแนวนอนหรือปุ่มที่เรียงต่อกัน ทั้งสองแบบใช้ร่วมกันได้ เช่น Grid จัดการ์ดสินค้าเป็นตาราง และ Flexbox จัดเนื้อหาภายในแต่ละการ์ด
auto-fit ไม่สร้างจำนวนคอลัมน์ตามที่คาดไว้ ต้องตรวจอะไร?
ตรวจสอบความกว้างของคอนเทนเนอร์ ค่าใน `minmax()` ระยะห่าง `gap` และความกว้างขั้นต่ำของรายการ หากคอนเทนเนอร์แคบจนไม่สามารถรองรับคอลัมน์ขั้นต่ำหลายคอลัมน์ได้ ระบบจะสร้างคอลัมน์น้อยลง ซึ่งเป็นพฤติกรรมปกติของเลย์เอาต์แบบยืดหยุ่น
Grid Items ต้องมีจำนวนเท่ากับจำนวนคอลัมน์หรือไม่?
ไม่จำเป็น หากมีรายการมากกว่าจำนวนคอลัมน์ รายการจะไหลไปยังแถวถัดไปตามกฎการจัดวางอัตโนมัติ หากจำนวนรายการไม่เต็มแถว พื้นที่ว่างอาจยังคงอยู่หรือถูกขยายตามการตั้งค่า Grid ที่เลือก
จะทำให้รูปภาพไม่ล้นการ์ดได้อย่างไร?
ใช้ `max-width: 100%` และ `height: auto` กับรูปภาพทั่วไป หากต้องการให้ภาพทุกใบมีขนาดพื้นที่เท่ากัน อาจกำหนดกรอบภาพและใช้ `object-fit: cover` เพื่อให้ภาพเต็มกรอบ โดยยอมให้ขอบบางส่วนถูกตัดตามอัตราส่วนที่กำหนด
12. แนวทางตรวจสอบก่อนนำ Grid ไปใช้จริง
ก่อนเผยแพร่หน้าเว็บ ควรตรวจสอบเลย์เอาต์ด้วยขนาดหน้าจอที่หลากหลาย ทดสอบเนื้อหาที่ยาวกว่าปกติ ตรวจดูว่าปุ่มยังใช้งานได้ด้วยคีย์บอร์ด รูปภาพไม่ล้นกรอบ และระยะห่างมีความสม่ำเสมอ นอกจากนี้ควรทดสอบกรณีมีรายการน้อยหรือมากกว่าที่คาดไว้ เพราะเนื้อหาจริงมักเปลี่ยนแปลงได้ตลอดเวลา
| รายการตรวจสอบ | สิ่งที่ควรทำ | เหตุผล |
|---|---|---|
| หน้าจอมือถือ | ตรวจสอบว่าคอลัมน์ลดลงตามพื้นที่ | ป้องกันเนื้อหาเบียดกัน |
| ข้อความยาว | ทดสอบชื่อสินค้าและคำอธิบายที่ยาว | ลดปัญหาความกว้างเกิน |
| รูปภาพ | กำหนดขนาดสูงสุดและตรวจอัตราส่วน | ป้องกันภาพล้นคอนเทนเนอร์ |
| การเข้าถึง | ใช้โครงสร้าง HTML ที่มีความหมายและปุ่มที่มีชื่อชัดเจน | ช่วยผู้ใช้เทคโนโลยีช่วยเหลือ |
| จำนวนรายการ | ทดลองทั้งกรณีมีรายการน้อยและหลายรายการ | ตรวจสอบการจัดวางอัตโนมัติ |
เลย์เอาต์ที่ดีไม่ใช่แค่การจัดองค์ประกอบให้สวยงาม แต่ต้องปรับตัวตามเนื้อหาและอุปกรณ์ของผู้ใช้ได้อย่างเหมาะสม
✅ สรุปบทเรียน EP10คุณได้เรียนรู้วิธีสร้างเลย์เอาต์ด้วย CSS Grid ตั้งแต่การกำหนดคอลัมน์และแถว การใช้ `repeat()` และ `gap` การจัดวางพื้นที่ด้วย `grid-template-areas` ไปจนถึงการสร้าง Responsive Grid ด้วย `minmax()`, `auto-fit` และ `auto-fill` ขั้นต่อไปคือทดลองปรับค่าต่าง ๆ และนำ Grid ไปใช้กับหน้าเว็บจริงของคุณ
13. แบบฝึกหัดท้ายบท
ลองปรับตัวอย่าง Product Grid ให้มีการ์ดอย่างน้อยหกใบ เปลี่ยนสีพื้นหลังของแต่ละการ์ด ทดลองเปลี่ยนค่า `minmax(220px, 1fr)` เป็น `minmax(260px, 1fr)` แล้วสังเกตว่าจำนวนคอลัมน์เปลี่ยนไปอย่างไร จากนั้นเพิ่มคลาส `featured-card` ให้การ์ดหนึ่งใบและกำหนด `grid-column: span 2` บนหน้าจอที่กว้างพอ พร้อมปรับเลย์เอาต์บนมือถือไม่ให้การ์ดกินพื้นที่เกินหน้าจอ
14. ตัวอย่างฉบับเต็ม: Responsive Grid Playground
โค้ดด้านล่างเป็นไฟล์ HTML แบบเต็มหน้า สามารถบันทึกเป็น `index.html` แล้วเปิดด้วยเบราว์เซอร์ได้ทันที ภายในมีตัวอย่างการ์ดที่ใช้ CSS Grid พร้อมปุ่มเปลี่ยนจำนวนคอลัมน์ เพื่อทดลองเปรียบเทียบเลย์เอาต์โดยไม่ต้องแก้โค้ดเองทุกครั้ง
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Grid Playground</title>
<style>
* { box-sizing: border-box; }
:root {
color-scheme: light;
--primary: #2563eb;
--ink: #172033;
--muted: #5b6475;
--surface: #ffffff;
--page: #f1f5f9;
--border: #dbe3ef;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: var(--ink);
background: var(--page);
line-height: 1.6;
}
.app { max-width: 1100px; margin: 0 auto; }
.hero {
padding: 24px;
border-radius: 18px;
color: #fff;
background: linear-gradient(135deg, #1d4ed8, #7c3aed);
margin-bottom: 20px;
}
.hero h1 { margin: 0 0 8px; line-height: 1.25; }
.hero p { margin: 0; color: #e0e7ff; }
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
padding: 16px;
margin-bottom: 20px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
}
.controls strong { margin-right: auto; }
button {
padding: 9px 14px;
border: 1px solid #bfdbfe;
border-radius: 9px;
background: #eff6ff;
color: #1d4ed8;
font: inherit;
font-weight: 700;
cursor: pointer;
}
button[aria-pressed="true"] { background: var(--primary); color: #fff; border-color: var(--primary); }
button:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 18px;
}
.card {
display: flex;
flex-direction: column;
min-width: 0;
padding: 18px;
border: 1px solid var(--border);
border-radius: 15px;
background: var(--surface);
box-shadow: 0 5px 18px rgba(15, 23, 42, .05);
transition: transform .2s ease, box-shadow .2s ease;
}
.card:hover { transform: translateY(-3px); box-shadow: 0 12px 24px rgba(15, 23, 42, .1); }
.icon {
display: grid;
place-items: center;
min-height: 100px;
border-radius: 12px;
background: #dbeafe;
font-size: 38px;
}
.card:nth-child(3n + 2) .icon { background: #ede9fe; }
.card:nth-child(3n) .icon { background: #dcfce7; }
.card h2 { margin: 15px 0 6px; font-size: 1.12rem; }
.card p { margin: 0 0 16px; color: var(--muted); }
.card .tag { margin-top: auto; font-size: .85rem; color: #1d4ed8; font-weight: 700; }
.status {
margin-top: 20px;
padding: 14px 16px;
border: 1px solid #bbf7d0;
border-radius: 12px;
background: #f0fdf4;
color: #166534;
}
.footer { margin-top: 24px; color: var(--muted); font-size: .9rem; text-align: center; }
@media (max-width: 520px) {
body { padding: 14px; }
.hero { padding: 18px; }
.controls { align-items: stretch; }
.controls strong { width: 100%; }
.controls button { flex: 1; }
.grid { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { transition-duration: .01ms !important; scroll-behavior: auto !important; }
}
</style>
</head>
<body>
<main class="app">
<header class="hero">
<h1>Responsive Grid Playground</h1>
<p>ทดลองจัดเลย์เอาต์ด้วย CSS Grid และเปรียบเทียบรูปแบบคอลัมน์</p>
</header>
<section class="controls" aria-label="ตัวเลือกจำนวนคอลัมน์">
<strong>เลือกจำนวนคอลัมน์บนหน้าจอขนาดใหญ่</strong>
<button type="button" data-columns="2" aria-pressed="false">2 คอลัมน์</button>
<button type="button" data-columns="3" aria-pressed="true">3 คอลัมน์</button>
<button type="button" data-columns="4" aria-pressed="false">4 คอลัมน์</button>
<button type="button" data-columns="auto" aria-pressed="false">อัตโนมัติ</button>
</section>
<section class="grid" id="grid" aria-label="ตัวอย่างการ์ด">
<article class="card"><div class="icon" aria-hidden="true">🧱</div><h2>Grid Container</h2><p>กำหนดกล่องแม่ให้เป็นระบบ Grid เพื่อจัดวางรายการลูก</p><span class="tag">display: grid</span></article>
<article class="card"><div class="icon" aria-hidden="true">📐</div><h2>Columns & Rows</h2><p>ควบคุมจำนวนคอลัมน์และแถวให้เหมาะกับโครงสร้างหน้าเว็บ</p><span class="tag">grid-template</span></article>
<article class="card"><div class="icon" aria-hidden="true">↔️</div><h2>Flexible Sizing</h2><p>ใช้หน่วย fr เพื่อแบ่งพื้นที่ว่างตามสัดส่วนที่ต้องการ</p><span class="tag">1fr 2fr</span></article>
<article class="card"><div class="icon" aria-hidden="true">📱</div><h2>Responsive Layout</h2><p>ให้จำนวนคอลัมน์ปรับตามพื้นที่และขนาดอุปกรณ์</p><span class="tag">auto-fit</span></article>
<article class="card"><div class="icon" aria-hidden="true">🧩</div><h2>Grid Areas</h2><p>ตั้งชื่อพื้นที่เพื่อทำให้ผังหน้าเว็บอ่านง่ายและแก้ไขสะดวก</p><span class="tag">grid-template-areas</span></article>
<article class="card"><div class="icon" aria-hidden="true">🎯</div><h2>Alignment</h2><p>จัดแนวรายการและควบคุมระยะห่างระหว่างองค์ประกอบ</p><span class="tag">place-items</span></article>
</section>
<p class="status" id="status" role="status" aria-live="polite">กำลังใช้เลย์เอาต์แบบอัตโนมัติ โดยปรับจำนวนคอลัมน์ตามพื้นที่หน้าจอ</p>
<p class="footer">ตัวอย่างเพื่อการเรียนรู้ HTML และ CSS — ปรับขนาดหน้าต่างเพื่อดู Responsive Layout</p>
</main>
<script>
const grid = document.getElementById('grid');
const status = document.getElementById('status');
const buttons = document.querySelectorAll('[data-columns]');
buttons.forEach((button) => {
button.addEventListener('click', () => {
const value = button.dataset.columns;
buttons.forEach((item) => {
item.setAttribute('aria-pressed', String(item === button));
});
if (value === 'auto') {
grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(220px, 1fr))';
status.textContent = 'เลือกโหมดอัตโนมัติ: เบราว์เซอร์จะปรับจำนวนคอลัมน์ตามพื้นที่ที่มี';
} else {
grid.style.gridTemplateColumns = 'repeat(' + value + ', minmax(0, 1fr))';
status.textContent = 'เลือก ' + value + ' คอลัมน์บนพื้นที่ที่รองรับ หากหน้าจอแคบมาก อาจต้องเลื่อนหรือปรับขนาดหน้าต่างเพื่อดูทุกคอลัมน์';
}
});
});
</script>
</body>
</html>
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง
⏳ กำลังจับเวลาอ่าน...