ตัวอย่างโค้ด
สร้างแกลเลอรีภาพที่คลิก thumbnail แล้วแสดงภาพขนาดใหญ่ใน Modal (Lightbox) พร้อมปุ่มปิดและรองรับ responsive
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Tabbed Image Gallery</title>
<style>
* { box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; margin: 0; padding: 20px; }
h1 { text-align: center; margin-bottom: 30px; }
/* Thumbnail Grid */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
max-width: 1000px;
margin: 0 auto;
}
.thumbnail {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
border-radius: 8px;
cursor: zoom-in;
transition: transform 0.2s, box-shadow 0.2s;
border: 3px solid transparent;
}
.thumbnail:hover {
transform: scale(1.02);
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
border-color: #7e32d4;
}
/* Modal */
.modal {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.9);
overflow: auto;
align-items: center;
justify-content: center;
padding: 20px;
}
.modal.open { display: flex; }
.modal-image {
max-width: 90%;
max-height: 85vh;
object-fit: contain;
border-radius: 4px;
box-shadow: 0 0 30px rgba(0,0,0,0.5);
animation: zoomIn 0.3s ease;
}
@keyframes zoomIn { from { transform: scale(0.9); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.close-btn {
position: absolute;
top: 20px; right: 30px;
color: white; font-size: 48px; font-weight: bold;
cursor: pointer; line-height: 1;
transition: color 0.2s, transform 0.2s;
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}
.close-btn:hover { color: #ff4444; transform: rotate(90deg); }
.caption {
color: #ccc; text-align: center; margin-top: 15px;
font-size: 18px; max-width: 90%;
}
/* Navigation arrows */
.nav-btn {
position: absolute;
top: 50%; transform: translateY(-50%);
background: rgba(255,255,255,0.2); border: none;
color: white; font-size: 40px; width: 60px; height: 60px;
border-radius: 50%; cursor: pointer;
transition: background 0.2s;
display: flex; align-items: center; justify-content: center;
}
.nav-btn:hover { background: rgba(255,255,255,0.4); }
.nav-btn.prev { left: 20px; }
.nav-btn.next { right: 20px; }
@media (max-width: 600px) { .nav-btn { width: 45px; height: 45px; font-size: 30px; } }
</style>
</head>
<body>
<h1>แกลเลอรีภาพแบบ Modal (Lightbox)</h1>
<div class="gallery">
<img class="thumbnail" src="https://picsum.photos/id/1015/400/400" alt="ภูเขา" data-caption="วิวภูเขาสวยงาม">
<img class="thumbnail" src="https://picsum.photos/id/237/400/400" alt="ทะเล" data-caption="ทะเลสีฟ้าใส">
<img class="thumbnail" src="https://picsum.photos/id/1025/400/400" alt="ป่า" data-caption="ป่าชื้นเขตร้อน">
<img class="thumbnail" src="https://picsum.photos/id/1035/400/400" alt="น้ำตก" data-caption="น้ำตกสูงสู่ฟ้า">
<img class="thumbnail" src="https://picsum.photos/id/1045/400/400" alt="ทะเลสาบ" data-caption="ทะเลสาบสงบ">
<img class="thumbnail" src="https://picsum.photos/id/1055/400/400" alt="อ่าว" data-caption="อ่าวสวยงาม">
</div>
<div class="modal" id="modal" role="dialog" aria-modal="true" aria-label="ภาพขยาย">
<button class="close-btn" aria-label="ปิด">×</button>
<button class="nav-btn prev" aria-label="ภาพก่อนหน้า">‹</button>
<img class="modal-image" id="modalImage" src="" alt="">
<button class="nav-btn next" aria-label="ภาพถัดไป">›</button>
<div class="caption" id="caption"></div>
</div>
<script>
const thumbnails = document.querySelectorAll('.thumbnail');
const modal = document.getElementById('modal');
const modalImage = document.getElementById('modalImage');
const caption = document.getElementById('caption');
const closeBtn = document.querySelector('.close-btn');
const prevBtn = document.querySelector('.nav-btn.prev');
const nextBtn = document.querySelector('.nav-btn.next');
let currentIndex = 0;
const images = Array.from(thumbnails);
function openModal(index) {
currentIndex = index;
const img = images[index];
modalImage.src = img.src.replace('/400/400', '/1200/800'); // โหลดรูปคุณภาพสูง
modalImage.alt = img.alt;
caption.textContent = img.dataset.caption || '';
modal.classList.add('open');
document.body.style.overflow = 'hidden'; // ป้องกัน scroll พื้นหลัง
updateNavButtons();
}
function closeModal() {
modal.classList.remove('open');
document.body.style.overflow = '';
}
function navigate(direction) {
currentIndex = (currentIndex + direction + images.length) % images.length;
openModal(currentIndex);
}
function updateNavButtons() {
// สำหรับ gallery แบบ loop ไม่ต้องซ่อนปุ่ม
}
// Event listeners
thumbnails.forEach((thumb, index) => {
thumb.addEventListener('click', () => openModal(index));
thumb.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openModal(index);
}
});
});
closeBtn.addEventListener('click', closeModal);
prevBtn.addEventListener('click', () => navigate(-1));
nextBtn.addEventListener('click', () => navigate(1));
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (!modal.classList.contains('open')) return;
if (e.key === 'Escape') closeModal();
if (e.key === 'ArrowLeft') navigate(-1);
if (e.key === 'ArrowRight') navigate(1);
});
// ปิดเมื่อคลิก backdrop
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal();
});
// Touch swipe support
let touchStartX = 0;
modal.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; });
modal.addEventListener('touchend', (e) => {
const touchEndX = e.changedTouches[0].clientX;
const diff = touchStartX - touchEndX;
if (Math.abs(diff) > 50) navigate(diff > 0 ? 1 : -1);
});
</script>
</body>
</html>
การทำงาน
- แสดง thumbnail เป็น Grid responsive (auto-fill)
- คลิก thumbnail → เปิด Modal แสดงภาพขนาดใหญ่ (โหลดรูปคุณภาพสูง)
- นำทางได้ด้วย: ปุ่ม Prev/Next, คีย์บอร์ด (← →), Swipe บนมือถือ
- ปิดได้ด้วย: ปุ่ม ×, Escape, คลิกนอกภาพ
- แสดง Caption จาก data attribute
ฟีเจอร์เพิ่มเติมจากต้นฉบับ
- Grid Layout - Responsive แบบทันสมัย แทน float
- Keyboard Navigation - รองรับ Arrow keys, Enter, Escape
- Touch Swipe - ลากซ้าย/ขวาเพื่อเปลี่ยนรูปบนมือถือ
- High-res Image - โหลดรูปคุณภาพสูงเมื่อเปิด modal
- Accessibility - ARIA labels, role, focus management
- Animation - Zoom in/out transition
- Body Scroll Lock - ป้องกัน scroll พื้นหลังขณะเปิด modal
โครงสร้างข้อมูล (Data Attributes)
<img class="thumbnail" src="thumb.jpg" alt="คำอธิบาย" data-caption="คำบรรยายภาพ" data-full="full-res.jpg">
ใช้ data-* attributes เก็บข้อมูลเพิ่มเติม แทนการ hardcode ใน JS