Beispiel:JS-Komfort-Bildwechsler.html
<!DOCTYPE html> <html lang="de"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" media="screen" href="./Beispiel:SELFHTML-Beispiel-Grundlayout.css"> <title>Komfort-Bildwechsler</title> <style>
.gallery {
& a { text-decoration: none; display: inline-block; &:focus { outline: 2px solid black; } & img { width: 100px; display: block; } }
}
- gallery-fullview {
width: 100vw; height: 100vh; padding: 2em 5vw 8vh 5vw; background-color: #888; box-sizing: border-box; border-radius: 10px;
&::backdrop { background: rgb(200 200 200 /.8) ; }
& figure { border: thin solid #aaa; margin: 0; box-sizing: border-box; width: 100%; height: 100%; background: #ccc; }
& img { display: block; margin: auto; width: 100%; height: 100%; object-fit: contain; }
& figcaption { font-style: italic; text-align: center; background-color: white; }
& button { position: absolute; width: 2rem; height: 2rem; cursor: pointer; border: 0; background-color: transparent; --fill: none; --stroke: white; &:focus { outline: none; } &[value=close], &[value=shuffle], &[value=play] { top: 0; right: 0; --fill: firebrick; &:hover, &:focus { --fill: red; } } &[value=shuffle], &[value=play] { & svg { &:nth-of-type(1) { display: inline; } &:nth-of-type(2) { display: none; } } } &[value=shuffle] { right: 4rem; .shuffle & svg { &:nth-of-type(1) { display: none; } &:nth-of-type(2) { display: inline; } } } &[value=play] { right: 8rem; .play & svg { &:nth-of-type(1) { display: none; } &:nth-of-type(2) { display: inline; } } }
&[value=prev], &[value=next] { top: calc(50% - 100px); width: 100px; height: 200px; padding: 0; --stroke: #fff6;
&:hover, &:focus { --stroke: skyblue; } } &[value=prev] { left: 2vw; } &[value=next] { right: 2vw; } }
}
.visually-hidden { position: absolute !important; clip: rect(1px, 1px, 1px, 1px) !important; padding: 0 !important; border: 0 !important; height: 1px !important; width: 1px !important; overflow: hidden !important; white-space: nowrap !important; }
</style> <script> 'use strict'; document.addEventListener('DOMContentLoaded', function () {
// Zeit in ms zum betrachten der Bilder im Automatik-Modus const zeitZumBetrachten = 1000;
// click-Handler auf alle Galerien legen. KÖNNTE man auch auf den Body legen und Bubbling nutzen, aber dann bekommt // man mit currentTarget die Galerie nicht geschenkt (und muss sie vom target mit closest(.gallery') suchen) document.querySelectorAll(".gallery") .forEach( gallery => gallery.addEventListener("click", handleGalleryClick) ); // EIGENEN click-Handler auf den Fullview. document.getElementById("gallery-fullview") .addEventListener("click", handleFullviewClickAndKeydown); // EIGENEN keydown-Handler aufs Dokument. document.addEventListener("keydown", handleFullviewClickAndKeydown);
// Klick in einer Galerie ist simpel, das behandelt alles die showInFullview Funktion, // die auch vom handleFullviewClickAndKeydown zum navigieren verwendet wird. function handleGalleryClick(event) { // event.target ist das img Element oder welches HTML auch immer sonst im Thumb steckt. Suche // das a Element als Bezugspunkt für die Fullview-Anzeige heraus const clickedLink = event.target.closest("a");
// Nicht auf einen Link geklickt? Nichts tun. if (!clickedLink) return; event.preventDefault();
// Delegiere den Rest... showInFullview(clickedLink); }
function showInFullview(link) { // der Fullscreen-Viewer const fullview = document.getElementById("gallery-fullview"), // die Galerie zum geklickten Link gallery = link.closest(".gallery"), // Das Thumbnail-Bild darin, brauchen wir für ... thumb = link.querySelector("img"), // ... den Caption-Text, der aus dem alt-Attribut generiert wird. caption = thumb ? thumb.alt : "";
// Den angeklickten Link zum aria-selected Element machen link.parentElement.querySelectorAll("a[aria-selected]").forEach(link => link.removeAttribute("aria-selected")); link.setAttribute("aria-selected", "true");
// src-Attribut im Vollbild auf das verlinkte Bild setzen und Caption in figcaption eintragen fullview.querySelector("img").src = link.href; fullview.querySelector("figcaption").textContent = caption;
// Galerie-ID speichern für click-Handler im Fullview und Fullview einblenden (falls noch nicht passiert) fullview.dataset.gallery = gallery.id; fullview.showModal(); }
function handleFullviewClickAndKeydown(event) { const fullview = document.getElementById("gallery-fullview") if(!fullview) return; const gallery = document.getElementById(fullview.dataset.gallery); if(!gallery) return; const currentThumb = gallery.querySelector("a[aria-selected]"); if (!currentThumb) return; let nextThumb,action="",stopped; if(event.type == "keydown") { action = event.key; // Welche Taste wurde gedrückt? } if(event.type == "click") { const clickedButton = event.target.closest("button"); // Nicht auf einen Button geklickt? Nichts tun. if (!clickedButton) return; action = clickedButton.value; // Welcher Button wurde geklickt? }
switch (action) { case 'close': case 'Escape': case 'x': stopAnimations(fullview); fullview.close(); fullview.dataset.gallery = null; gallery.querySelector("a[aria-selected]").focus(); break; case 'prev': case 'ArrowLeft': // Ausführliche Version nextThumb = navigateDOM(currentThumb, elem => elem.previousElementSibling, elem => elem.tagName == "A"); if (!nextThumb) nextThumb = gallery.querySelector("a:last-of-type")
showInFullview(nextThumb); break; case 'next': case 'ArrowRight': // Kompaktversion als Einzeiler showInFullview(navigateDOM(currentThumb, elem => elem.nextElementSibling, elem => elem.tagName == "A") || gallery.querySelector("a:first-of-type")); break; case 'play': case 'r': // Evtl. laufende Animationen benden stopped = stopAnimations(fullview); if (!stopped.playStopped) { // Mit setInterval die Bilder nacheinander zeigen (next animieren) fullview.play = setInterval(function() { showInFullview(navigateDOM(gallery.querySelector("a[aria-selected]"), elem => elem.nextElementSibling, elem => elem.tagName == "A") || gallery.querySelector("a:first-of-type")); }, zeitZumBetrachten); fullview.classList.add("play"); } break; case 'shuffle': case 's': // Evtl. laufende Animationen benden stopped = stopAnimations(fullview); if (!stopped.shuffleStopped) { // Per Zufall das nächste Bilde ermitteln und anzeigen const allThumbs = gallery.querySelectorAll("a"); let randomNumber,lastNumber=-1; fullview.shuffle = setInterval(function() { do { randomNumber = Math.floor(Math.random()*allThumbs.length); } while(randomNumber == lastNumber) showInFullview(allThumbs[randomNumber]); lastNumber = randomNumber; }, zeitZumBetrachten); fullview.classList.add("shuffle"); } break; case 'p': // Evtl. laufende Animationen benden stopAnimations(fullview); break; } /* Helper: Navigiere schrittweise durch's DOM, bis eine Bedingung erfüllt ist * "current" - Ausgangspunkt (ein HTML Elemnt) * Was das "nächste" Thumbnail-Element ist, legt der proceed-Callback fest. * Die Funktion sucht ausdrücklich nach a-Elementen - wenn andere Elemente im * Container sind, werden sie übersprungen. Wird kein Link mehr gefunden, gibt * die Funktion null zurück - um den Rest kümmere sich bitte der Aufrufer. */ function navigateDOM(current, proceed, checkFound) { if (!current) return null; while (current = proceed(current)) { if (checkFound(current)) break; } return current; } /* Helper: Beende evtl. laufende Animationen und gebe zurück, * welche Animation beendet wurde. */ function stopAnimations(fullviewElement) { let playStopped = false, shuffleStopped = false; if(fullview.play) { clearInterval(fullviewElement.play); fullviewElement.play = null; fullviewElement.classList.remove("play"); playStopped = true; } if(fullviewElement.shuffle) { clearInterval(fullviewElement.shuffle); fullviewElement.shuffle = null; fullviewElement.classList.remove("shuffle"); shuffleStopped = true; } return { "playStopped": playStopped, "shuffleStopped": shuffleStopped }; }
}
}); </script>
</head> <body>
Komfort-Bildwechsler
<section id="peru" class="gallery">
Peru 2007
<a href="https://wiki.selfhtml.org/images/2/28/Peru-1.jpg"> <img src="https://wiki.selfhtml.org/images/2/24/Peru-1-sm.jpg" alt="Peru 2007: Cusco - Blick auf Ausangate"> </a> <a href="https://wiki.selfhtml.org/images/4/42/Peru-2.jpg"> <img src="https://wiki.selfhtml.org/images/e/ea/Peru-2-sm.jpg" alt="Peru 2007: Valle Sagrado"> </a> <a href="https://wiki.selfhtml.org/images/a/ab/Peru-3.jpg"> <img src="https://wiki.selfhtml.org/images/c/c5/Peru-3-sm.jpg" alt="Peru 2007: Machu Picchu"> </a> <a href="https://wiki.selfhtml.org/images/8/80/Peru-4.jpg"> <img src="https://wiki.selfhtml.org/images/b/b0/Peru-4-sm.jpg" alt="Peru 2007: Machu Picchu - Lamas in den Ruinen"> </a> <a href="https://wiki.selfhtml.org/images/3/3f/Peru-5.jpg"> <img src="https://wiki.selfhtml.org/images/7/71/Peru-5-sm.jpg" alt="Peru 2007: Uros-Inseln im Titicaca-See"> </a> <a href="https://wiki.selfhtml.org/images/4/41/Peru-6.jpg"> <img src="https://wiki.selfhtml.org/images/5/5b/Peru-6-sm.jpg" alt="Peru 2007: Ceviche - Meeresfrüchte mit Zitronensaft"> </a>
</section>
<section id="gardasee" class="gallery">
Gardasee 2016
<a href="https://wiki.selfhtml.org/images/f/fb/Gardasee1.jpg"> <img src="https://wiki.selfhtml.org/images/8/8f/Gardasee1-sm.jpg" alt="Gardasee 2016: Sonnenuntergang bei Bardolino "> </a> <a href="https://wiki.selfhtml.org/images/1/16/Gardasee2.jpg"> <img src="https://wiki.selfhtml.org/images/6/67/Gardasee2-sm.jpg" alt="Gardasee 2016: Aperol Spritz - Entspannung für Lehrer "> </a> <a href="https://wiki.selfhtml.org/images/3/3b/Gardasee-3.jpg"> <img src="https://wiki.selfhtml.org/images/4/40/Gardasee-3-sm.jpg" alt="Gardasee 2016: Gardaland bei Bardolino - Blick von oben"> </a> <a href="https://wiki.selfhtml.org/images/e/ef/Gardasee-4.jpg"> <img src="https://wiki.selfhtml.org/images/1/16/Gardasee-4-sm.jpg" alt="Gardasee 2016: Wasserrutsche im Gardaland "> </a> <a href="https://wiki.selfhtml.org/images/1/15/Gardasee-5.jpg"> <img src="https://wiki.selfhtml.org/images/c/c3/Gardasee-5-sm.jpg" alt="Gardasee 2016: Sonnenuntergang bei Bardolino"> </a> <a href="https://wiki.selfhtml.org/images/5/5a/Gardasee-6.jpg"> <img src="https://wiki.selfhtml.org/images/d/d4/Gardasee-6-sm.jpg" alt="Gardasee 2016: nächtliches Handballspiel "> </a> <a href="https://wiki.selfhtml.org/images/2/2e/Gardasee-7.jpg"> <img src="https://wiki.selfhtml.org/images/a/a4/Gardasee-7-sm.jpg" alt="Gardasee 2016: Die antike Arena in Verona - heute für Konzerte und Opern genutzt "> </a>
</section>
Bei einem Klick auf die Thumbnail-Vorschau erhalten Sie eine Großansicht.
<dialog id="gallery-fullview">
<figure> <img alt="described by figcaption" src='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3C/svg%3E'> <figcaption></figcaption> </figure> <button type="button" value="close">Schließen <svg viewBox='0 0 100 100'> <rect width='100' height='100' fill='var(--fill)' /> <path d='M20,20 l60,60 m0,-60 l-60,60' fill='none' stroke='white' stroke-width='15' stroke-linecap='round'/> </svg> </button> <button type="button" value="play" aria-label="play/pause animation">automatisch abspielen <svg viewBox='0 0 200 200'> <rect width='200' height='200' fill='var(--fill)' /> <path d='M60,30 l90,70 -90,70z' fill='white' stroke='white' stroke-width='30' stroke-linejoin='round' /> </svg> <svg viewBox='0 0 100 100'> <rect width='100' height='100' fill='var(--fill)' /> <path d='M30,20 L30,80 M70,20 L70,80' fill='none' stroke='white' stroke-width='25' stroke-linecap='round'/> </svg> </button> <button type="button" value="shuffle">shuffle <svg viewBox='0 0 376 376'> <rect x='0' y='0' width='100%' height='100%' fill='var(--fill)' stroke='black' stroke-width='1'/> <path d='M376 280l-79 68v-45h-13c-42 0-73-19-98-44 10-12 19-23 27-34 2-3 4-5 6-7 19 19 39 33 66 33h13v-38L376 280zM0 129h39c25 0 44 12 62 29 3-4 6-8 9-12 7-10 15-20 23-30 -25-23-55-40-95-40H0V129zM297 28v45h-13c-69 0-108 51-143 97 -31 41-58 76-101 76H0v53h39c69 0 108-51 143-97 31-41 58-76 101-76h13v38l79-68L297 28z' fill='white'/> </svg> <svg viewBox='0 0 100 100'> <rect width='100' height='100' fill='var(--fill)' /> <path d='M30,20 L30,80 M70,20 L70,80' fill='none' stroke='white' stroke-width='25' stroke-linecap='round'/> </svg> </button> <button type="button" value="prev">vorheriges Bild <svg width='100' height='200' viewBox='0 0 100 200' xmlns='http://www.w3.org/2000/svg'> <path id='left' d='M90,10 l-80,90 l80,90' fill='var(--fill)' stroke='var(--stroke)' stroke-width='15' stroke-linecap='round'/> </svg> </button> <button type="button" value="next">nächstes Bild <svg width='100' height='200' viewBox='0 0 100 200'> <path id='right' d='M10,10 l80,90 l-80,90' fill='var(--fill)' stroke='var(--stroke)' stroke-width='15' stroke-linecap='round'/> </svg> </button>
</dialog>
</body> </html>