สร้าง Off-Canvas Menu (Side Navigation) ที่สไลด์ออกมาจากขอบจอซ้าย ทับเนื้อหาหรือผลักเนื้อหาไปข้างๆ
โค้ดตัวอย่างสมบูรณ์
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Off-Canvas Side Menu</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: 'Segoe UI', Tahoma, sans-serif;
overflow-x: hidden; /* ป้องกัน Scroll แนวนอน */
}
/* Side Navigation */
.sidenav {
height: 100vh;
width: 0; /* เริ่มต้นซ่อน (กว้าง 0) */
position: fixed;
z-index: 1000;
top: 0;
left: 0;
background-color: #2edada; /* ฟ้าอ่อน */
overflow-x: hidden;
padding-top: 80px;
transition: width 0.4s ease; /* Animation ความกว้าง */
}
.sidenav a {
display: block;
padding: 12px 16px 12px 32px;
text-decoration: none;
font-size: 20px;
color: #000;
font-family: inherit;
transition: color 0.3s, background-color 0.3s;
}
.sidenav a:hover {
color: #f1f1f1;
background-color: rgba(255,255,255,0.1);
}
/* ปุ่มปิด */
.sidenav .closebtn {
position: absolute;
top: 16px;
right: 24px;
font-size: 32px;
color: #000;
text-decoration: none;
padding: 0;
width: auto;
height: auto;
line-height: 1;
}
.sidenav .closebtn:hover {
color: #f1f1f1;
background: none;
}
/* ปุ่มเปิดเมนู */
.openbtn {
padding: 16px 24px;
background-color: #001b91;
color: white;
font-size: 18px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.openbtn:hover {
background-color: #00146b;
}
/* เนื้อหาหลัก - ผลักไปข้างๆ */
.main-content {
transition: margin-left 0.4s ease;
padding: 24px;
}
/* Overlay สำหรับ Mobile (คลิกเพื่อปิด) */
.overlay {
display: none;
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 999;
}
@media (max-width: 768px) {
.overlay.active { display: block; }
}
</style>
</head>
<body>
<!-- Side Navigation -->
<nav class="sidenav" id="sidenav">
<a href="#" class="closebtn" aria-label="Close Menu">×</a>
<a href="#">เข้าสู่ระบบ</a>
<a href="#">สมัครสมาชิก</a>
<a href="#">หน้าแรก</a>
<a href="#">เกี่ยวกับเรา</a>
</nav>
<!-- Overlay สำหรับ Mobile -->
<div class="overlay" id="overlay"></div>
<!-- เนื้อหาหลัก -->
<main class="main-content">
<button class="openbtn" id="openBtn">☰ เปิดเมนู</button>
<h1>ตัวอย่าง Off-Canvas Menu</h1>
<p>กดปุ่ม "เปิดเมนู" เพื่อดู Side Navigation สไลด์ออกมา</p>
<p>เนื้อหาหลักจะถูกผลักไปทางขวาพร้อมกัน</p>
</main>
<script>
const sidenav = document.getElementById('sidenav');
const openBtn = document.getElementById('openBtn');
const closeBtn = sidenav.querySelector('.closebtn');
const overlay = document.getElementById('overlay');
const mainContent = document.querySelector('.main-content');
const NAV_WIDTH = '280px';
function showNav() {
sidenav.style.width = NAV_WIDTH;
mainContent.style.marginLeft = NAV_WIDTH;
overlay.classList.add('active');
document.body.style.overflow = 'hidden'; // ป้องกัน Scroll Body
}
function hideNav() {
sidenav.style.width = '0';
mainContent.style.marginLeft = '0';
overlay.classList.remove('active');
document.body.style.overflow = '';
}
// Event Listeners
openBtn.addEventListener('click', showNav);
closeBtn.addEventListener('click', hideNav);
overlay.addEventListener('click', hideNav);
// ปิดเมนูด้วยปุ่ม Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') hideNav();
});
// ปิดเมนูเมื่อคลิกลิงก์ (Optional)
sidenav.querySelectorAll('a:not(.closebtn)').forEach(link => {
link.addEventListener('click', hideNav);
});
</script>
</body>
</html>
หลักการทำงาน
- CSS: Side Nav เริ่มต้น
width: 0,transition: width 0.4s - JavaScript: Toggle ความกว้างระหว่าง
0และ280px - Main Content: ใช้
margin-leftผลักเนื้อหาไปข้างขวา (Push Pattern) - Overlay: สำหรับ Mobile - คลิกนอกเมนูเพื่อปิด, ป้องกัน Scroll Body
รูปแบบการทำงาน (Patterns)
| Pattern | ลักษณะ | CSS หลัก |
|---|---|---|
| Push (ตัวอย่างนี้) | ผลักเนื้อหาหลักไปข้างๆ | sidenav width + main margin-left |
| Overlay | ทับเนื้อหา (z-index สูงกว่า) | sidenav width, main ไม่ย้าย |
| Reveal | เนื้อหาหลักอยู่คงที่, เมนูอยู่ใต้ | body transform, sidenav fixed |
ปรับปรุงสำหรับ Production
// 1. ใช้ CSS Class แทน Inline Styles
function showNav() {
document.body.classList.add('sidenav-open');
}
function hideNav() {
document.body.classList.remove('sidenav-open');
}
/* CSS */
body.sidenav-open .sidenav { width: 280px; }
body.sidenav-open .main-content { margin-left: 280px; }
body.sidenav-open .overlay { display: block; }
body.sidenav-open { overflow: hidden; }
// 2. ใช้ Transform แทน Width (Performance - GPU Accelerated)
.sidenav {
transform: translateX(-100%);
transition: transform 0.4s ease;
}
body.sidenav-open .sidenav {
transform: translateX(0);
}
body.sidenav-open .main-content {
transform: translateX(280px);
}
Accessibility (A11y)
aria-labelบนปุ่มเปิด/ปิดrole="dialog"+aria-modal="true"บน Side Nav- Focus Trap - จำกัด Tab ให้อยู่ในเมนูขณะเปิด
- Escape Key เพื่อปิด