Files
KantSites/zentrale/public/app.js
T
kantarciandClaude Opus 5.5 bb736e8258
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Make the registry search live
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:13:38 +02:00

119 lines
4.7 KiB
JavaScript

// 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));
})();