/* Generischer Karussell-Antrieb für alle [data-carousel]-Elemente (Werbe-/Sponsoren-Banner und Matchcenter „Nächste Spiele"). Auto-Rotation über die Fortschrittsanimation des aktiven Indikators (animationend → nächste Folie). Erwartete Hooks je Wurzel: [data-carousel] Wurzel [data-carousel-track] Folien-Container (bekommt aria-live) [data-carousel-slide] einzelne Folie [data-carousel-controls] Steuerleiste (ohne JS hidden) [data-carousel-toggle] Pause/Play-Taste [data-carousel-toggle-label] visually-hidden Beschriftung [data-carousel-dot] Indikator-Button [data-carousel-progress] Fortschritts-Element im aktiven Dot (treibt den Wechsel) Barrierefreiheit: - explizite Pause/Play-Taste (WCAG 2.2.2 „Pause, Stop, Hide"), - zusätzlich Pause bei Hover/Fokus, - aria-live: im Autoplay "off", bei Pause/manuell "polite", - bei prefers-reduced-motion läuft keine Animation → keine Auto-Rotation; Indikatoren bleiben per Klick bedienbar, die Pause-Taste entfällt. Ohne JS ist nur die erste Folie sichtbar (Graceful Degradation). */ (function () { 'use strict'; function initCarousel(root) { var track = root.querySelector('[data-carousel-track]'); var slides = Array.prototype.slice.call(root.querySelectorAll('[data-carousel-slide]')); var dots = Array.prototype.slice.call(root.querySelectorAll('[data-carousel-dot]')); var controls = root.querySelector('[data-carousel-controls]'); var toggle = root.querySelector('[data-carousel-toggle]'); var toggleLabel = root.querySelector('[data-carousel-toggle-label]'); if (slides.length <= 1) { return; } var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; var index = 0; var hoverPaused = false; var userPaused = false; function syncPaused() { var paused = hoverPaused || userPaused || reduce; root.classList.toggle('is-paused', paused); if (track) { track.setAttribute('aria-live', paused ? 'polite' : 'off'); } } function activate(i) { index = (i % slides.length + slides.length) % slides.length; slides.forEach(function (slide, n) { slide.classList.toggle('is-active', n === index); }); dots.forEach(function (dot, n) { var on = n === index; dot.classList.toggle('is-active', on); if (on) { dot.setAttribute('aria-current', 'true'); } else { dot.removeAttribute('aria-current'); } }); } // Fortschrittsanimation des aktiven Indikators durchgelaufen → nächste Folie. // (Beim Wechsel bricht die alte Animation ab → 'animationcancel' statt // 'animationend' → kein Doppelsprung.) root.addEventListener('animationend', function (event) { if (event.target && event.target.matches && event.target.matches('[data-carousel-progress]')) { activate(index + 1); } }); root.addEventListener('mouseenter', function () { hoverPaused = true; syncPaused(); }); root.addEventListener('mouseleave', function () { hoverPaused = false; syncPaused(); }); root.addEventListener('focusin', function () { hoverPaused = true; syncPaused(); }); root.addEventListener('focusout', function (event) { if (!root.contains(event.relatedTarget)) { hoverPaused = false; syncPaused(); } }); dots.forEach(function (dot, n) { dot.addEventListener('click', function () { activate(n); }); }); // Steuerung einblenden (ohne JS bleibt sie verborgen, da funktionslos). if (controls) { controls.hidden = false; } // Pause/Play nur anbieten, wenn überhaupt automatisch rotiert wird. if (toggle && !reduce) { toggle.hidden = false; toggle.addEventListener('click', function () { userPaused = !userPaused; toggle.setAttribute('aria-pressed', String(userPaused)); if (toggleLabel) { toggleLabel.textContent = userPaused ? 'Automatischen Wechsel fortsetzen' : 'Automatischen Wechsel pausieren'; } syncPaused(); }); } // Crossfade aktivieren: Slides stapeln (CSS) und Sichtbarkeit per Klasse // steuern. Vorher gesetzte hidden-Attribute (No-JS-Fallback) entfernen. root.classList.add('is-enhanced'); slides.forEach(function (slide) { slide.hidden = false; }); activate(0); syncPaused(); } Array.prototype.slice.call(document.querySelectorAll('[data-carousel]')).forEach(initCarousel); })();