สร้าง Tab Headers แบบคลาสสิก - แท็บอยู่ด้านบน เนื้อหาแสดงด้านล่าง เปลี่ยนแท็บโดยการ คลิก (Click)
โค้ดตัวอย่างสมบูรณ์
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tab Headers Example</title>
<style>
* { box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
padding: 24px;
max-width: 800px;
margin: 0 auto;
background: #f5f5f5;
}
/* Tab Buttons Container */
.tab-headers {
display: flex;
border-bottom: 2px solid #ddd;
margin-bottom: 0;
background: #fff;
border-radius: 8px 8px 0 0;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.tab-btn {
flex: 1;
padding: 16px 24px;
background: transparent;
border: none;
border-bottom: 3px solid transparent;
cursor: pointer;
font-size: 16px;
font-weight: 500;
color: #666;
transition: all 0.2s ease;
position: relative;
}
.tab-btn:hover {
color: #333;
background: #f8f9fa;
}
.tab-btn.active {
color: #0066cc;
border-bottom-color: #0066cc;
background: #fff;
}
.tab-btn.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 3px;
background: #0066cc;
}
/* Tab Content Panels */
.tab-content {
background: #fff;
border-radius: 0 0 8px 8px;
padding: 32px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
min-height: 200px;
}
.tab-panel {
display: none;
animation: fadeIn 0.25s ease;
}
.tab-panel.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.tab-panel h3 {
margin-top: 0;
margin-bottom: 16px;
color: #333;
}
.tab-panel p {
color: #555;
line-height: 1.7;
}
</style>
</head>
<body>
<h2>ตารางการแข่งขัน</h2>
<!-- Tab Headers -->
<div class="tab-headers" role="tablist">
<button class="tab-btn active" role="tab" aria-selected="true" aria-controls="panel-date" onclick="switchTab('date', this, '#e74c3c')">
📅 วันที่
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-time" onclick="switchTab('time', this, '#f39c12')">
🕐 เวลา
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-venue" onclick="switchTab('venue', this, '#27ae60')">
📍 สถานที่
</button>
</div>
<!-- Tab Panels -->
<div class="tab-content">
<div id="panel-date" class="tab-panel active" role="tabpanel">
<h3>📅 วันที่การแข่งขัน</h3>
<p>การแข่งขันจะจัดขึ้นวันที่ 2 เมษายน 2567 (วันอังคาร)</p>
<p>เปิดรับการลงทะเบียนตั้งแต่วันที่ 1 มีนาคม 2567 เป็นต้นไป</p>
</div>
<div id="panel-time" class="tab-panel" role="tabpanel">
<h3>🕐 เวลาการแข่งขัน</h3>
<p>เริ่มต้นเวลา 09:00 น. จนจบประมาณ 18:00 น.</p>
<p>การลงทะเบียนรายวันเริ่มเวลา 08:00 น. ก่อนเปิดประตูสนาม</p>
</div>
<div id="panel-venue" class="tab-panel" role="tabpanel">
<h3>📍 สถานที่จัดการแข่งขัน</h3>
<p>สนามกีฬาเมลเบิร์น คริเก็ต 그라운드 (MCG) เมลเบิร์น, ออสเตรเลีย</p>
<p>ที่นั่ง 100,000 ที่นั่ง ใกล้สถานีรถไฟ Richmond Station</p>
</div>
</div>
<script>
function switchTab(panelId, btnElement, activeColor) {
// 1. ซ่อนทุก Panel
document.querySelectorAll('.tab-panel').forEach(panel => {
panel.classList.remove('active');
});
// 2. Reset ทุกปุ่ม
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
btn.setAttribute('aria-selected', 'false');
btn.style.borderBottomColor = 'transparent';
});
// 3. แสดง Panel ที่เลือก
const targetPanel = document.getElementById('panel-' + panelId);
if (targetPanel) {
targetPanel.classList.add('active');
}
// 4. Active ปุ่มที่คลิก
btnElement.classList.add('active');
btnElement.setAttribute('aria-selected', 'true');
btnElement.style.borderBottomColor = activeColor;
}
// Auto-activate first tab on load
document.addEventListener('DOMContentLoaded', () => {
const firstBtn = document.querySelector('.tab-btn.active');
if (firstBtn) {
firstBtn.click(); // Trigger click เพื่อให้ Active state ถูกต้อง
}
});
</script>
</body>
</html>
โครงสร้าง HTML แบบ Semantic (ARIA Pattern)
<div role="tablist" aria-label="Match Schedule"> <button role="tab" aria-selected="true" aria-controls="panel-date" id="tab-date">Date</button> <button role="tab" aria-selected="false" aria-controls="panel-time" id="tab-time">Time</button> <button role="tab" aria-selected="false" aria-controls="panel-venue" id="tab-venue">Venue</button> </div> <div role="tabpanel" id="panel-date" aria-labelledby="tab-date">...</div> <div role="tabpanel" id="panel-time" aria-labelledby="tab-time" hidden>...</div> <div role="tabpanel" id="panel-venue" aria-labelledby="tab-venue" hidden>...</div>
JavaScript Module Pattern (Reusable)
class TabManager {
constructor(containerSelector) {
this.container = document.querySelector(containerSelector);
this.tabs = this.container.querySelectorAll('[role="tab"]');
this.panels = this.container.querySelectorAll('[role="tabpanel"]');
this.init();
}
init() {
this.tabs.forEach(tab => {
tab.addEventListener('click', () => this.activateTab(tab));
tab.addEventListener('keydown', (e) => this.handleKeydown(e));
});
// Activate first tab
this.activateTab(this.tabs[0]);
}
activateTab(selectedTab) {
const panelId = selectedTab.getAttribute('aria-controls');
this.tabs.forEach(tab => {
tab.setAttribute('aria-selected', 'false');
tab.classList.remove('active');
});
this.panels.forEach(panel => {
panel.hidden = true;
panel.classList.remove('active');
});
selectedTab.setAttribute('aria-selected', 'true');
selectedTab.classList.add('active');
const targetPanel = document.getElementById(panelId);
if (targetPanel) {
targetPanel.hidden = false;
targetPanel.classList.add('active');
}
}
handleKeydown(e) {
const tabs = Array.from(this.tabs);
const currentIndex = tabs.indexOf(e.target);
let nextIndex = currentIndex;
switch(e.key) {
case 'ArrowRight': nextIndex = (currentIndex + 1) % tabs.length; break;
case 'ArrowLeft': nextIndex = (currentIndex - 1 + tabs.length) % tabs.length; break;
case 'Home': nextIndex = 0; break;
case 'End': nextIndex = tabs.length - 1; break;
default: return;
}
e.preventDefault();
tabs[nextIndex].focus();
this.activateTab(tabs[nextIndex]);
}
}
// Usage
new TabManager('.tab-headers');
การปรับแต่งสไตล์ (Variations)
| Style | CSS Key Points |
|---|---|
| Underline Indicator | border-bottom บน .active + ::after pseudo-element |
| Background Fill | background-color เปลี่ยนสีพื้นหลังแท็บ Active |
| Pill/Rounded | border-radius บนปุ่ม, Container ไม่มี Border Bottom |
| Minimal/Line | เฉพาะเส้นใต้ Active, ไม่มี Border รอบ Container |
| Card Style | แต่ละแท็บเป็น Card แยก, Hover Elevate |