// Live-Suche der Übersicht: Tippen, Filter und Seitenwechsel laden nur die Liste nach. // Ohne JavaScript bleibt alles ein gewöhnliches Formular mit Links. (() => { const form = document.querySelector('form.suche'); const liste = document.getElementById('liste'); if (!form || !liste || !window.fetch || !window.URL) { return; } const feld = form.querySelector('#q'); const status = document.getElementById('treffer'); const zuruecksetzen = form.querySelector('.zuruecksetzen'); const knopf = form.querySelector('button[type="submit"]'); const filterFeld = () => form.querySelector('input[name="filter"]'); knopf.hidden = true; let warten = 0; let laufend = null; /** Die Adresse aus Suchfeld und Filter, ohne leere Werte. */ const adresseAusFormular = () => { const url = new URL(form.action, location.href); const q = feld.value.trim(); if (q !== '') url.searchParams.set('q', q); const filter = filterFeld(); if (filter && filter.value !== '') url.searchParams.set('filter', filter.value); return url; }; /** Kacheln und Filterfeld an Suche und Filter der neuen Adresse angleichen. */ const umgebungAnpassen = (url) => { const q = url.searchParams.get('q') || ''; const filter = url.searchParams.get('filter') || ''; document.querySelectorAll('.zahlen a').forEach((a) => { const ziel = new URL(a.href, location.href); if (q === '') ziel.searchParams.delete('q'); else ziel.searchParams.set('q', q); ziel.searchParams.delete('seite'); a.href = ziel.pathname + ziel.search; const gewaehlt = (ziel.searchParams.get('filter') || '') === filter; a.parentElement.classList.toggle('gewaehlt', gewaehlt); if (gewaehlt) a.setAttribute('aria-current', 'true'); else a.removeAttribute('aria-current'); }); let feldFilter = filterFeld(); if (filter !== '' && !feldFilter) { feldFilter = document.createElement('input'); feldFilter.type = 'hidden'; feldFilter.name = 'filter'; form.prepend(feldFilter); } if (feldFilter) { if (filter === '') feldFilter.remove(); else feldFilter.value = filter; } if (feld.value.trim() !== q) feld.value = q; zuruecksetzen.hidden = q === '' && filter === ''; }; const laden = (url, verlauf) => { if (laufend) laufend.abort(); laufend = new AbortController(); const abruf = new URL(url, location.href); abruf.searchParams.set('teil', 'liste'); liste.setAttribute('aria-busy', 'true'); fetch(abruf, { signal: laufend.signal, credentials: 'same-origin', headers: { Accept: 'text/html' } }) .then((antwort) => { // Abgelaufene Sitzung: Die Antwort ist die Anmeldeseite, nicht die Liste. if (!antwort.ok || antwort.redirected) throw new Error('voll'); return antwort.text(); }) .then((html) => { const neu = new DOMParser().parseFromString(html, 'text/html').getElementById('liste'); if (!neu) throw new Error('voll'); liste.replaceChildren(...neu.childNodes); liste.dataset.treffer = neu.dataset.treffer || ''; status.textContent = liste.dataset.treffer; const sichtbar = new URL(url, location.href); if (verlauf === 'push') history.pushState(null, '', sichtbar.pathname + sichtbar.search); else if (verlauf === 'replace') history.replaceState(null, '', sichtbar.pathname + sichtbar.search); umgebungAnpassen(sichtbar); }) .catch((fehler) => { if (fehler.name !== 'AbortError') location.href = url.toString(); }) .finally(() => liste.removeAttribute('aria-busy')); }; feld.addEventListener('input', () => { clearTimeout(warten); warten = setTimeout(() => laden(adresseAusFormular(), 'replace'), 250); }); form.addEventListener('submit', (ereignis) => { ereignis.preventDefault(); clearTimeout(warten); laden(adresseAusFormular(), 'replace'); }); zuruecksetzen.addEventListener('click', (ereignis) => { ereignis.preventDefault(); feld.value = ''; const filter = filterFeld(); if (filter) filter.remove(); laden(new URL('/', location.href), 'push'); feld.focus(); }); // Filterkacheln und Seitenzahlen: nur die Liste wechseln. document.addEventListener('click', (ereignis) => { const link = ereignis.target.closest('.zahlen a, #liste .seiten a'); if (!link || ereignis.ctrlKey || ereignis.metaKey || ereignis.shiftKey || ereignis.button !== 0) return; ereignis.preventDefault(); laden(new URL(link.href, location.href), 'push'); if (link.closest('.seiten')) { form.scrollIntoView({ block: 'start' }); liste.focus({ preventScroll: true }); } }); window.addEventListener('popstate', () => laden(new URL(location.href), null)); })();