the home site for me: also iteration 3 or 4 of my site
1class ImageLightbox extends HTMLElement {
2 constructor() {
3 super();
4 this.currentImages = [];
5 this.currentIndex = 0;
6 this.handleImageClick = this.handleImageClick.bind(this);
7 this.handleKeyPress = this.handleKeyPress.bind(this);
8 }
9
10 connectedCallback() {
11 this.render();
12 this.setupEventListeners();
13 }
14
15 disconnectedCallback() {
16 this.cleanup();
17 }
18
19 render() {
20 this.id = 'lightbox';
21 this.style.display = 'none';
22 this.innerHTML = `
23 <div class="lightbox-content">
24 <button class="lightbox-close" aria-label="Close lightbox">×</button>
25 <img id="lightbox-img" src="" alt="" />
26 <div class="lightbox-controls">
27 <button class="lightbox-prev" aria-label="Previous image">←</button>
28 <button class="lightbox-next" aria-label="Next image">→</button>
29 </div>
30 </div>
31 `;
32 }
33
34 setupEventListeners() {
35 // Delegate clicks on images with data-lightbox attribute
36 document.addEventListener('click', this.handleImageClick);
37
38 // Lightbox controls
39 this.querySelector('.lightbox-close').addEventListener('click', () => this.close());
40 this.querySelector('.lightbox-prev').addEventListener('click', () => this.prev());
41 this.querySelector('.lightbox-next').addEventListener('click', () => this.next());
42
43 // Click backdrop to close
44 this.addEventListener('click', (e) => {
45 if (e.target === this) this.close();
46 });
47
48 // Keyboard navigation
49 document.addEventListener('keydown', this.handleKeyPress);
50 }
51
52 handleImageClick(e) {
53 // Check if clicked element or its parent has data-lightbox
54 const target = e.target.closest('[data-lightbox]');
55 if (!target) return;
56
57 e.preventDefault();
58
59 const group = target.dataset.lightboxGroup;
60
61 if (group) {
62 // Find all images in the same group
63 const groupElements = Array.from(
64 document.querySelectorAll(`[data-lightbox-group="${group}"]`)
65 );
66
67 // Extract image sources
68 this.currentImages = groupElements.map(el => {
69 const img = el.tagName === 'IMG' ? el : el.querySelector('img');
70 return img ? img.src : null;
71 }).filter(Boolean);
72
73 // Find the index of the clicked image
74 this.currentIndex = groupElements.indexOf(target);
75 } else {
76 // Single image
77 const img = target.tagName === 'IMG' ? target : target.querySelector('img');
78 this.currentImages = img ? [img.src] : [];
79 this.currentIndex = 0;
80 }
81
82 if (this.currentImages.length > 0) {
83 this.open();
84 }
85 }
86
87 handleKeyPress(e) {
88 if (this.style.display !== 'flex') return;
89
90 if (e.key === 'Escape') {
91 this.close();
92 } else if (e.key === 'ArrowLeft') {
93 this.prev();
94 } else if (e.key === 'ArrowRight') {
95 this.next();
96 }
97 }
98
99 open() {
100 this.style.display = 'flex';
101 const controls = this.querySelector('.lightbox-controls');
102 if (this.currentImages.length > 1) {
103 controls.style.display = 'flex';
104 } else {
105 controls.style.display = 'none';
106 }
107 document.body.style.overflow = 'hidden';
108 this.updateImage();
109 }
110
111 close() {
112 this.style.display = 'none';
113 document.body.style.overflow = '';
114 }
115
116 updateImage() {
117 const img = this.querySelector('#lightbox-img');
118 img.src = this.currentImages[this.currentIndex];
119 }
120
121 prev() {
122 this.currentIndex = (this.currentIndex - 1 + this.currentImages.length) % this.currentImages.length;
123 this.updateImage();
124 }
125
126 next() {
127 this.currentIndex = (this.currentIndex + 1) % this.currentImages.length;
128 this.updateImage();
129 }
130
131 cleanup() {
132 document.removeEventListener('click', this.handleImageClick);
133 document.removeEventListener('keydown', this.handleKeyPress);
134 document.body.style.overflow = '';
135 }
136}
137
138customElements.define('image-lightbox', ImageLightbox);
139
140// Auto-initialize: add lightbox to page if not already present
141if (document.readyState === 'loading') {
142 document.addEventListener('DOMContentLoaded', initLightbox);
143} else {
144 initLightbox();
145}
146
147function initLightbox() {
148 if (!document.querySelector('image-lightbox')) {
149 const lightbox = document.createElement('image-lightbox');
150 document.body.appendChild(lightbox);
151 }
152}