สร้าง Vertical Tabs - แท็บจัดเรียงแนวตั้งด้านซ้าย เนื้อหาแสดงด้านขวา เหมาะสำหรับ Sidebar Navigation, Settings Panel, Documentation
โค้ดตัวอย่างสมบูรณ์
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vertical Tab Menu</title>
<style>
* { box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
margin: 0;
padding: 24px;
background: #f5f5f5;
}
.vertical-tabs {
display: flex;
max-width: 900px;
margin: 0 auto;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
overflow: hidden;
}
/* Tab Sidebar - แนวตั้ง */
.tab-sidebar {
width: 220px;
min-width: 220px;
background: #1a1a2e; /* สีเข้ม */
border-right: 1px solid #2d2d44;
}
.tab-btn {
display: block;
width: 100%;
padding: 18px 24px;
background: transparent;
border: none;
border-left: 3px solid transparent;
text-align: left;
cursor: pointer;
font-size: 15px;
font-weight: 500;
color: #a0a0b0;
transition: all 0.25s ease;
font-family: inherit;
}
.tab-btn:hover {
background: rgba(255,255,255,0.05);
color: #fff;
}
.tab-btn.active {
background: rgba(0, 100, 0, 0.15); /* สีเขียวอ่อน */
border-left-color: #006400;
color: #00c851;
font-weight: 600;
}
.tab-btn:focus {
outline: none;
box-shadow: inset 3px 0 0 #006400;
}
/* Content Area */
.tab-content-area {
flex: 1;
padding: 40px;
min-height: 350px;
}
.tab-panel {
display: none;
animation: slideIn 0.3s ease;
}
.tab-panel.active {
display: block;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.tab-panel h3 {
margin: 0 0 16px;
color: #1a1a2e;
font-size: 24px;
}
.tab-panel p {
color: #555;
line-height: 1.8;
font-size: 16px;
}
/* Responsive: Mobile Stack */
@media (max-width: 768px) {
.vertical-tabs {
flex-direction: column;
}
.tab-sidebar {
width: 100%;
border-right: none;
border-bottom: 1px solid #2d2d44;
}
.tab-btn {
border-left: none;
border-bottom: 3px solid transparent;
padding: 14px 20px;
}
.tab-btn.active {
border-left-color: transparent;
border-bottom-color: #006400;
background: rgba(0, 100, 0, 0.1);
}
.tab-content-area {
padding: 24px;
min-height: auto;
}
}
</style>
</head>
<body>
<h2>ข้อมูลการสอบ</h2>
<p>ข้อมูลเกี่ยวกับการสอบครั้งนี้:</p>
<div class="vertical-tabs">
<div class="tab-sidebar" role="tablist" aria-label="Exam Information">
<button class="tab-btn active" role="tab" aria-selected="true" aria-controls="panel-dates" onclick="showInfo('dates', this)">
📅 วันสอบ
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-centre" onclick="showInfo('centre', this)">
📍 สถานที่สอบ
</button>
</div>
<div class="tab-content-area">
<div id="panel-dates" class="tab-panel active" role="tabpanel">
<h3>📅 วันสอบ</h3>
<p>วันสอบจะถูกประกาศอย่างเป็นทางการวันที่ 25 มีนาคม 2567 โปรดติดตามข่าวสารเพิ่มเติม</p>
<p>การสอบจะดำเนินการต่อเนื่องเป็นเวลา 3 วัน ตั้งแต่วันที่ประกาศ</p>
</div>
<div id="panel-centre" class="tab-panel" role="tabpanel">
<h3>📍 สถานที่สอบ</h3>
<p>สถานที่สอบจะถูกประกาศวันที่ 30 มีนาคม 2567 ผู้สมัครสามารถตรวจสอบได้ที่เว็บไซต์ทางการ</p>
<p>จะมีสถานที่สอบกระจายอยู่หลายจังหวัด ให้เลือกสถานที่ที่สะดวกที่สุด</p>
</div>
</div>
</div>
<script>
function showInfo(infoId, btnElement) {
// ซ่อนทุก Panel
document.querySelectorAll('.tab-panel').forEach(panel => {
panel.classList.remove('active');
});
// Reset ทุกปุ่ม
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
btn.setAttribute('aria-selected', 'false');
});
// แสดง Panel ที่เลือก
const targetPanel = document.getElementById('panel-' + infoId);
if (targetPanel) {
targetPanel.classList.add('active');
}
// Active ปุ่มที่คลิก
btnElement.classList.add('active');
btnElement.setAttribute('aria-selected', 'true');
}
// Auto-activate first tab
document.addEventListener('DOMContentLoaded', () => {
const firstBtn = document.querySelector('.tab-btn.active');
if (firstBtn) firstBtn.click();
});
</script>
</body>
</html>
หลักการทำงาน
- Layout: Flex Container → Sidebar (Fixed Width) + Content Area (Flex: 1)
- Tab Buttons:
display: block; width: 100%จัดแนวตั้ง - Active Indicator:
border-left(Desktop) /border-bottom(Mobile) - JS Logic: เหมือน Horizontal Tabs - Toggle Class
active
การปรับปรุงสำหรับ Production
1. Keyboard Navigation (Arrow Up/Down)
document.querySelectorAll('.tab-btn').forEach((btn, index, tabs) => {
btn.addEventListener('keydown', (e) => {
let nextIndex = index;
if (e.key === 'ArrowDown') nextIndex = (index + 1) % tabs.length;
else if (e.key === 'ArrowUp') nextIndex = (index - 1 + tabs.length) % tabs.length;
else if (e.key === 'Home') nextIndex = 0;
else if (e.key === 'End') nextIndex = tabs.length - 1;
else return;
e.preventDefault();
tabs[nextIndex].focus();
tabs[nextIndex].click();
});
});
2. Nested Tabs (Sub-tabs)
/* CSS สำหรับ Sub-tabs */
.tab-btn.has-submenu { position: relative; }
.tab-btn.has-submenu::after {
content: '▶';
float: right;
font-size: 10px;
transition: transform 0.2s;
}
.tab-btn.has-submenu.open::after { transform: rotate(90deg); }
.submenu {
display: none;
padding-left: 16px;
border-left: 2px solid #333;
background: rgba(0,0,0,0.1);
}
.submenu.open { display: block; }
3. Persist Active Tab (URL Hash / localStorage)
// Save to URL Hash
function showInfo(infoId, btnElement) {
// ... switching logic ...
history.replaceState(null, '', '#' + infoId); // Update URL without reload
}
// Restore from Hash on Load
document.addEventListener('DOMContentLoaded', () => {
const hash = location.hash.slice(1); // Remove #
if (hash) {
const btn = document.querySelector(`[aria-controls="panel-${hash}"]`);
if (btn) btn.click();
}
});
กรณีใช้งานที่เหมาะสม
- Settings / Preferences Panels
- Documentation Sidebar
- Admin Dashboard Navigation
- Multi-step Forms / Wizards
- Product Feature Comparison
- Help / FAQ Categories