Der Knopf "nach oben" ist ein Anker, kein Skript: er springt an den Seitenanfang, eingeblendet ueber eine Scroll-Zeitachse. Abschaltbar und vollstaendig einstellbar - Seite, Form, Groesse, Farben, Schwelle, Beschriftung. Bilder lassen sich vergroessern, je Bild waehlbar (nichts / vergroessern / Adresse), Galerien mit einem Schalter. Grundlage ist ein gewoehnlicher Verweis auf die grosse Fassung: ohne JavaScript oeffnet er das Bild, mit JavaScript einen dialog-Betrachter mit Blaettern, Escape und Pfeiltasten. Das Skript laedt nur, wo es etwas zu tun hat. Die Rechtszeile wird ein Baustein und steht damit im Fussbereich statt als heller Streifen darunter. Die eingebaute Leiste erscheint nur noch, wenn die Seite den Baustein nicht enthaelt. Dabei gefunden: zwei Stylesheet-Regeln griffen seit drei Veroeffentlichungen nicht mehr. Beim Entfernen alter Regeln waren Selektorreste stehen geblieben, und CSS nimmt dann die naechste Regel mit - betroffen waren der Einwilligungsdialog und die Rechtszeile. Ein Test prueft jetzt jede CSS-Datei auf abgebrochene Selektoren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102 lines
3.9 KiB
JavaScript
102 lines
3.9 KiB
JavaScript
/*
|
||
* Der Bildbetrachter.
|
||
*
|
||
* Ohne dieses Skript ist ein vergrößerbares Bild schlicht ein Verweis auf die große Fassung: ein
|
||
* Klick öffnet sie, zurück führt die Zurück-Taste. Das ist die Grundlage, und sie funktioniert
|
||
* immer. Dieses Skript fängt den Klick ab und zeigt das Bild stattdessen über der Seite - mit
|
||
* Blättern, wenn es zu einer Galerie gehört.
|
||
*
|
||
* Benutzt wird <dialog>: der Browser bringt damit das Fangen des Fokus, das Schließen mit Escape
|
||
* und die Auszeichnung als Dialog schon mit. Eine nachgebaute Fassung aus <div> und Tastatur-
|
||
* abfragen wäre mehr Code und weniger zugänglich.
|
||
*/
|
||
(() => {
|
||
const zoomable = [...document.querySelectorAll('a[data-zoom]')];
|
||
if (0 === zoomable.length) {
|
||
return;
|
||
}
|
||
|
||
const dialog = document.createElement('dialog');
|
||
dialog.className = 'zoom-dialog';
|
||
dialog.innerHTML = `
|
||
<figure class="zoom-figure">
|
||
<img class="zoom-image" alt="">
|
||
<figcaption class="zoom-caption"></figcaption>
|
||
</figure>
|
||
<button class="zoom-close" type="button" aria-label="Schließen">×</button>
|
||
<button class="zoom-prev" type="button" aria-label="Vorheriges Bild">‹</button>
|
||
<button class="zoom-next" type="button" aria-label="Nächstes Bild">›</button>
|
||
<p class="zoom-counter" aria-live="polite"></p>
|
||
`;
|
||
document.body.append(dialog);
|
||
|
||
const bild = dialog.querySelector('.zoom-image');
|
||
const beschriftung = dialog.querySelector('.zoom-caption');
|
||
const zaehler = dialog.querySelector('.zoom-counter');
|
||
const zurueck = dialog.querySelector('.zoom-prev');
|
||
const vor = dialog.querySelector('.zoom-next');
|
||
|
||
/** Die Bilder, zwischen denen geblättert wird: die der Galerie, sonst nur dieses eine. */
|
||
const gruppeVon = (link) => {
|
||
const galerie = link.closest('[data-zoom-group]');
|
||
|
||
return galerie ? [...galerie.querySelectorAll('a[data-zoom]')] : [link];
|
||
};
|
||
|
||
let gruppe = [];
|
||
let stelle = 0;
|
||
|
||
const zeige = (index) => {
|
||
stelle = (index + gruppe.length) % gruppe.length;
|
||
const link = gruppe[stelle];
|
||
const inneres = link.querySelector('img');
|
||
bild.src = link.href;
|
||
bild.alt = inneres ? inneres.alt : '';
|
||
const text = link.dataset.caption || '';
|
||
beschriftung.textContent = text;
|
||
beschriftung.hidden = '' === text;
|
||
const mehrere = gruppe.length > 1;
|
||
zurueck.hidden = !mehrere;
|
||
vor.hidden = !mehrere;
|
||
zaehler.hidden = !mehrere;
|
||
zaehler.textContent = mehrere ? `${stelle + 1} von ${gruppe.length}` : '';
|
||
};
|
||
|
||
for (const link of zoomable) {
|
||
link.addEventListener('click', event => {
|
||
// Mit gedrückter Zusatztaste oder mittlerer Maustaste bleibt es ein normaler Verweis:
|
||
// wer ein Bild in einem neuen Reiter öffnen will, soll das können.
|
||
if (event.metaKey || event.ctrlKey || event.shiftKey || 1 === event.button) {
|
||
return;
|
||
}
|
||
event.preventDefault();
|
||
gruppe = gruppeVon(link);
|
||
zeige(gruppe.indexOf(link));
|
||
dialog.showModal();
|
||
});
|
||
}
|
||
|
||
zurueck.addEventListener('click', () => zeige(stelle - 1));
|
||
vor.addEventListener('click', () => zeige(stelle + 1));
|
||
dialog.querySelector('.zoom-close').addEventListener('click', () => dialog.close());
|
||
// Ein Klick neben das Bild schließt - erwartet, und der Dialog selbst ist die Fläche daneben.
|
||
dialog.addEventListener('click', event => {
|
||
if (event.target === dialog) {
|
||
dialog.close();
|
||
}
|
||
});
|
||
dialog.addEventListener('keydown', event => {
|
||
if (1 === gruppe.length) {
|
||
return;
|
||
}
|
||
if ('ArrowLeft' === event.key) {
|
||
event.preventDefault();
|
||
zeige(stelle - 1);
|
||
}
|
||
if ('ArrowRight' === event.key) {
|
||
event.preventDefault();
|
||
zeige(stelle + 1);
|
||
}
|
||
});
|
||
})();
|