152 lines
5.5 KiB
JavaScript
152 lines
5.5 KiB
JavaScript
|
|
/* Lightbox: rüstet eine Bildergalerie ([data-lightbox-gallery]) von normalen
|
||
|
|
Bildlinks auf eine Großansicht im Overlay um. Ohne JS bleibt jedes
|
||
|
|
Vorschaubild ein Link auf die größte Variante der Datei — der Klick führt
|
||
|
|
dann einfach zum Bild, nichts ist kaputt oder tot.
|
||
|
|
|
||
|
|
Bewusst generisch gehalten (keine „veranstaltung"-Bezüge, Hooks nur über
|
||
|
|
data-lightbox-*), damit eine zweite Galerie auf der Seite dieselbe Datei
|
||
|
|
nutzen kann — dasselbe Prinzip wie beim generischen carousel.js.
|
||
|
|
|
||
|
|
Das Overlay-Markup liefert PHP und ist `hidden`, bis JS es übernimmt (wie die
|
||
|
|
Steuerung des Stadionzeitungs-Viewers). Die Großansicht erbt ihr srcset vom
|
||
|
|
angeklickten Vorschaubild: so lädt ein Handy die kleine Variante und es gibt
|
||
|
|
nur EINE Bildliste im Markup. */
|
||
|
|
(function () {
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
function initGallery(root) {
|
||
|
|
var links = Array.prototype.slice.call(root.querySelectorAll('[data-lightbox]'));
|
||
|
|
var dialog = root.querySelector('[data-lightbox-dialog]');
|
||
|
|
|
||
|
|
if (!dialog || links.length === 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var image = dialog.querySelector('[data-lightbox-img]');
|
||
|
|
var counter = dialog.querySelector('[data-lightbox-current]');
|
||
|
|
var closeBtn = dialog.querySelector('[data-lightbox-close]');
|
||
|
|
var prevBtn = dialog.querySelector('[data-lightbox-prev]');
|
||
|
|
var nextBtn = dialog.querySelector('[data-lightbox-next]');
|
||
|
|
|
||
|
|
if (!image || !closeBtn) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var index = 0;
|
||
|
|
var isOpen = false;
|
||
|
|
|
||
|
|
function show(i) {
|
||
|
|
index = Math.max(0, Math.min(links.length - 1, i));
|
||
|
|
var thumb = links[index].querySelector('img');
|
||
|
|
|
||
|
|
image.src = links[index].getAttribute('href');
|
||
|
|
image.srcset = thumb ? thumb.getAttribute('srcset') || '' : '';
|
||
|
|
image.alt = thumb ? thumb.getAttribute('alt') || '' : '';
|
||
|
|
|
||
|
|
if (counter) {
|
||
|
|
counter.textContent = String(index + 1);
|
||
|
|
}
|
||
|
|
if (prevBtn) {
|
||
|
|
prevBtn.disabled = index === 0;
|
||
|
|
}
|
||
|
|
if (nextBtn) {
|
||
|
|
nextBtn.disabled = index === links.length - 1;
|
||
|
|
}
|
||
|
|
// Am ersten/letzten Bild wird der gerade fokussierte Knopf disabled
|
||
|
|
// und der Fokus fiele auf <body> — dann griffe die Fokusfalle unten
|
||
|
|
// nicht mehr und Tab liefe hinter das Overlay.
|
||
|
|
if (document.activeElement && document.activeElement.disabled) {
|
||
|
|
closeBtn.focus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function onKeydown(event) {
|
||
|
|
if (event.key === 'Escape') {
|
||
|
|
event.preventDefault();
|
||
|
|
hide();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (event.key === 'ArrowLeft') {
|
||
|
|
event.preventDefault();
|
||
|
|
show(index - 1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (event.key === 'ArrowRight') {
|
||
|
|
event.preventDefault();
|
||
|
|
show(index + 1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (event.key !== 'Tab') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fokusfalle: im Overlay sind nur diese Knöpfe erreichbar.
|
||
|
|
var focusable = [prevBtn, nextBtn, closeBtn].filter(function (btn) {
|
||
|
|
return btn && !btn.disabled;
|
||
|
|
});
|
||
|
|
var first = focusable[0];
|
||
|
|
var last = focusable[focusable.length - 1];
|
||
|
|
|
||
|
|
if (event.shiftKey && document.activeElement === first) {
|
||
|
|
event.preventDefault();
|
||
|
|
last.focus();
|
||
|
|
} else if (!event.shiftKey && document.activeElement === last) {
|
||
|
|
event.preventDefault();
|
||
|
|
first.focus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function reveal(i) {
|
||
|
|
show(i);
|
||
|
|
dialog.hidden = false;
|
||
|
|
// Hintergrund festhalten, sonst scrollt die Seite unter dem Overlay
|
||
|
|
// weg (gleiches Muster wie body.nav-open beim Burger-Menü).
|
||
|
|
document.body.classList.add('lightbox-open');
|
||
|
|
document.addEventListener('keydown', onKeydown);
|
||
|
|
isOpen = true;
|
||
|
|
closeBtn.focus();
|
||
|
|
}
|
||
|
|
|
||
|
|
function hide() {
|
||
|
|
if (!isOpen) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
dialog.hidden = true;
|
||
|
|
document.body.classList.remove('lightbox-open');
|
||
|
|
document.removeEventListener('keydown', onKeydown);
|
||
|
|
isOpen = false;
|
||
|
|
// Fokus zurück auf das ZULETZT gezeigte Vorschaubild, nicht auf das
|
||
|
|
// zuerst angeklickte: nach mehrmals Weiter ist das die Stelle, an
|
||
|
|
// der man im Raster gerade steht.
|
||
|
|
links[index].focus();
|
||
|
|
}
|
||
|
|
|
||
|
|
links.forEach(function (link, i) {
|
||
|
|
link.addEventListener('click', function (event) {
|
||
|
|
event.preventDefault();
|
||
|
|
reveal(i);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
if (prevBtn) {
|
||
|
|
prevBtn.addEventListener('click', function () { show(index - 1); });
|
||
|
|
}
|
||
|
|
if (nextBtn) {
|
||
|
|
nextBtn.addEventListener('click', function () { show(index + 1); });
|
||
|
|
}
|
||
|
|
closeBtn.addEventListener('click', hide);
|
||
|
|
|
||
|
|
dialog.addEventListener('click', function (event) {
|
||
|
|
// Klick auf die Fläche neben dem Bild schließt; Klicks auf Bild und
|
||
|
|
// Knöpfe nicht.
|
||
|
|
if (event.target === dialog) {
|
||
|
|
hide();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
root.classList.add('is-enhanced');
|
||
|
|
}
|
||
|
|
|
||
|
|
Array.prototype.slice.call(document.querySelectorAll('[data-lightbox-gallery]')).forEach(initGallery);
|
||
|
|
})();
|