class ImageLightbox extends HTMLElement { constructor() { super(); this.currentImages = []; this.currentIndex = 0; this.handleImageClick = this.handleImageClick.bind(this); this.handleKeyPress = this.handleKeyPress.bind(this); } connectedCallback() { this.render(); this.setupEventListeners(); } disconnectedCallback() { this.cleanup(); } render() { this.id = 'lightbox'; this.style.display = 'none'; this.innerHTML = ` `; } setupEventListeners() { // Delegate clicks on images with data-lightbox attribute document.addEventListener('click', this.handleImageClick); // Lightbox controls this.querySelector('.lightbox-close').addEventListener('click', () => this.close()); this.querySelector('.lightbox-prev').addEventListener('click', () => this.prev()); this.querySelector('.lightbox-next').addEventListener('click', () => this.next()); // Click backdrop to close this.addEventListener('click', (e) => { if (e.target === this) this.close(); }); // Keyboard navigation document.addEventListener('keydown', this.handleKeyPress); } handleImageClick(e) { // Check if clicked element or its parent has data-lightbox const target = e.target.closest('[data-lightbox]'); if (!target) return; e.preventDefault(); const group = target.dataset.lightboxGroup; if (group) { // Find all images in the same group const groupElements = Array.from( document.querySelectorAll(`[data-lightbox-group="${group}"]`) ); // Extract image sources this.currentImages = groupElements.map(el => { const img = el.tagName === 'IMG' ? el : el.querySelector('img'); return img ? img.src : null; }).filter(Boolean); // Find the index of the clicked image this.currentIndex = groupElements.indexOf(target); } else { // Single image const img = target.tagName === 'IMG' ? target : target.querySelector('img'); this.currentImages = img ? [img.src] : []; this.currentIndex = 0; } if (this.currentImages.length > 0) { this.open(); } } handleKeyPress(e) { if (this.style.display !== 'flex') return; if (e.key === 'Escape') { this.close(); } else if (e.key === 'ArrowLeft') { this.prev(); } else if (e.key === 'ArrowRight') { this.next(); } } open() { this.style.display = 'flex'; const controls = this.querySelector('.lightbox-controls'); if (this.currentImages.length > 1) { controls.style.display = 'flex'; } else { controls.style.display = 'none'; } document.body.style.overflow = 'hidden'; this.updateImage(); } close() { this.style.display = 'none'; document.body.style.overflow = ''; } updateImage() { const img = this.querySelector('#lightbox-img'); img.src = this.currentImages[this.currentIndex]; } prev() { this.currentIndex = (this.currentIndex - 1 + this.currentImages.length) % this.currentImages.length; this.updateImage(); } next() { this.currentIndex = (this.currentIndex + 1) % this.currentImages.length; this.updateImage(); } cleanup() { document.removeEventListener('click', this.handleImageClick); document.removeEventListener('keydown', this.handleKeyPress); document.body.style.overflow = ''; } } customElements.define('image-lightbox', ImageLightbox); // Auto-initialize: add lightbox to page if not already present if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initLightbox); } else { initLightbox(); } function initLightbox() { if (!document.querySelector('image-lightbox')) { const lightbox = document.createElement('image-lightbox'); document.body.appendChild(lightbox); } }