119 lines
4.7 KiB
JavaScript
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));
|
|
})();
|