fix(quellen): Suche ohne <form>, Click + Enter direkt binden

Browser-Quirk: in einem <form> blieb der fetch nach Submit hängen,
auch mit preventDefault() im submit-Handler. Status-Text bekam
"Suche läuft …", aber die Response kam nie an — der Browser hat den
fetch durch den Submit-State des Forms blockiert.

Lösung: <form> → <div>, Button auf type="button". Click direkt an
runSearch binden, Enter via keydown auf dem Input. Keine Form-Submit-
Semantik mehr.

Bei E2E-Smoketest mit Playwright reproduzierbar gefixt.
This commit is contained in:
Dotty Dotter 2026-05-09 01:09:45 +02:00
parent 37941f0a2b
commit e48cab6db3

View File

@ -196,11 +196,11 @@
Wortunscharf — Endungen sind egal, verwandte Begriffe werden ebenfalls
gefunden.
</p>
<form id="quellen-search-form" class="search-box">
<div id="quellen-search-form" class="search-box">
<input type="text" id="quellen-q" name="q" placeholder="z.B. Klimaschutz, soziale Gerechtigkeit, Mietpreisbremse"
autocomplete="off" minlength="2" maxlength="200">
<button type="submit" id="quellen-q-btn">Suchen</button>
</form>
<button type="button" id="quellen-q-btn">Suchen</button>
</div>
<div class="search-filter" style="margin-top:6px;">
<label><input type="radio" name="qfilter" value="current" checked> nur aktuelle Programme</label>
<label><input type="radio" name="qfilter" value="all"> auch historische</label>
@ -318,8 +318,7 @@ function escHtml(s) {
.replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
async function runSearch(ev) {
if (ev) ev.preventDefault();
async function runSearch() {
const q = document.getElementById('quellen-q').value.trim();
const filter = (document.querySelector('input[name="qfilter"]:checked') || {}).value || 'current';
const statusEl = document.getElementById('quellen-search-status');
@ -380,25 +379,29 @@ async function runSearch(ev) {
return false;
}
// Form-Submit + Filter-Wechsel binden.
// Direkt im body_scripts-Block — wenn das Script läuft, ist DOMContentLoaded
// häufig schon vorbei (Script kommt am Ende des Body). Daher kein
// addEventListener('DOMContentLoaded', …) als Wrapper.
// Suche binden — kein <form>, sondern Click auf Button + Enter im Input.
// Frühere <form>-Variante hatte einen Browser-Quirk: trotz preventDefault()
// im submit-Handler hat der anschließende fetch() nie eine Response
// erhalten (Status blieb auf "Suche läuft …"). Mit einem Plain-Container +
// Button onclick funktioniert es zuverlässig.
//
// Wichtig: `runSearch` ist async und returnt Promise<false>. Das ist truthy,
// also würde `onsubmit="return runSearch(event)"` das Default-Submit NICHT
// verhindern, der fetch würde mit "Failed to fetch" abbrechen, weil die Page
// navigiert. Stattdessen über addEventListener binden — da bleibt
// `event.preventDefault()` synchron, bevor der Promise resolvt.
// Direkt im body_scripts-Block — wenn das Script läuft, ist DOMContentLoaded
// häufig schon vorbei. Daher kein addEventListener('DOMContentLoaded', …)
// als Wrapper, sondern IIFE mit Idempotenz-Marker.
(function bindQuellenSearch() {
const form = document.getElementById('quellen-search-form');
if (form && !form._quellenBound) {
form._quellenBound = true;
form.addEventListener('submit', (ev) => {
const btn = document.getElementById('quellen-q-btn');
if (btn && !btn._quellenBound) {
btn._quellenBound = true;
btn.addEventListener('click', () => runSearch());
}
const input = document.getElementById('quellen-q');
if (input && !input._quellenBound) {
input._quellenBound = true;
input.addEventListener('keydown', (ev) => {
if (ev.key === 'Enter') {
ev.preventDefault();
// Microtask-Queue erst leeren, sonst hängt der fetch in manchen
// Browsern beobachtbar (Submit-State blockt anschließenden fetch).
setTimeout(runSearch, 0);
runSearch();
}
});
}
document.querySelectorAll('input[name="qfilter"]').forEach(r => {