Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

วิธีสร้างแกลเลอรีภาพแบบแท็บ (Tabbed Image Gallery) ด้วย CSS และ JavaScript


ตัวอย่างโค้ด

สร้างแกลเลอรีภาพที่คลิก 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