Wissen: Kategoriewahl und Filter ohne Systemmenue

Filipe: "wieso sieht das so scheisse aus?" -- zum aufgeklappten
Auswahlmenue der Kategorie. Er hat recht, und die Ursache ist dieselbe
wie bei der Rollenwahl: Ein AUFGEKLAPPTES <select> laesst sich nicht
gestalten. Es kommt vom Betriebssystem, in Windows-Weiss, mitten in
einer dunklen Oberflaeche. Bei zwanzig Eintraegen kam dazu, dass man in
so einer Liste nichts findet.

KATEGORIEWAHL: jetzt dieselben sechs Welten, dieselben Farben und
dieselben Symbole wie die Kacheln auf der Seite. Man waehlt aus
demselben Regal, in dem man sonst stoebert. Unter der Wahl steht die
Beschreibung der gewaehlten Kategorie -- die Zeile, die beim
Einsortieren die eigentliche Frage beantwortet.

Nichts ist vorausgewaehlt (ausser man laedt aus einer Kategorie heraus
hoch). Die stille Vorbelegung war der Grund, warum eine PDF ueber
TikTok LIVE Studio unter "TikTok - Grundlagen" landete.

FILTER Stufe und Geraet: ebenfalls Schalter statt Menue. Zwei bis vier
Werte, die sich nie aendern -- dafuer ein Menue zu oeffnen ist ein Klick
zu viel.

Der Tag-Filter bleibt bewusst ein Auswahlfeld: Tags wachsen mit dem
Bestand, eine Schalterreihe waere irgendwann zwei Zeilen lang.

Geprueft: 20 Kategorien in 6 Welten, nichts vorausgewaehlt, Filter
setzen die Adresse (?stufe=einsteiger), Handy ohne Ueberlauf, 0
Konsolenfehler. Testeintrag landete unter der gewaehlten Kategorie.
This commit is contained in:
2026-08-31 12:14:18 +02:00
parent 389d8c1213
commit e14d9d5042
15 changed files with 244 additions and 112 deletions
+74 -32
View File
@@ -284,28 +284,44 @@
let filterGefuellt = false;
function fuelleFilter() {
const z = zustand();
const setz = (id, eintraege, wert) => {
/* Stufe und Geraet als Schalterreihe: zwei bis vier Werte, die sich
nie aendern -- dafuer ein Auswahlmenue zu oeffnen ist ein Klick zu
viel, und aufgeklappt liesse es sich ohnehin nicht gestalten. */
const reihe = (id, eintraege, wert, feldname) => {
const f = $(id);
f.textContent = '';
const alle = document.createElement('option');
alle.value = ''; alle.textContent = 'Alle';
f.append(alle);
for (const [w, name] of eintraege) {
const o = document.createElement('option');
o.value = w; o.textContent = name;
f.append(o);
for (const [w, name] of [['', 'Alle'], ...eintraege]) {
const b = el('button', 'filterreihe__knopf', name);
b.type = 'button';
b.setAttribute('role', 'radio');
const an = (wert || '') === w;
b.setAttribute('aria-checked', an ? 'true' : 'false');
if (an) b.dataset.an = 'ja';
if (w) b.dataset.wert = w;
b.addEventListener('click', () => setzeZustand({ [feldname]: w }));
f.append(b);
}
f.value = wert || '';
};
setz('f-stufe', Object.entries(aufbau.stufen), z.stufe);
setz('f-geraet', [['pc', 'PC'], ['handy', 'Handy']], z.geraet);
setz('f-tag', aufbau.tags.map((t) => [t, '#' + t]), z.tag);
reihe('f-stufe', Object.entries(aufbau.stufen), z.stufe, 'stufe');
reihe('f-geraet', [['pc', 'PC'], ['handy', 'Handy']], z.geraet, 'geraet');
/* Die Tags wachsen mit dem Bestand -- dafuer bleibt ein Auswahlfeld
richtig, eine Schalterreihe waere irgendwann zwei Zeilen lang. */
const f = $('f-tag');
f.textContent = '';
const alle = document.createElement('option');
alle.value = ''; alle.textContent = 'Alle';
f.append(alle);
for (const tg of aufbau.tags) {
const o = document.createElement('option');
o.value = tg; o.textContent = '#' + tg;
f.append(o);
}
f.value = z.tag || '';
$('q').value = z.q;
if (filterGefuellt) return;
filterGefuellt = true;
$('f-stufe').addEventListener('change', () => setzeZustand({ stufe: $('f-stufe').value }));
$('f-geraet').addEventListener('change', () => setzeZustand({ geraet: $('f-geraet').value }));
$('f-tag').addEventListener('change', () => setzeZustand({ tag: $('f-tag').value }));
$('f-weg').addEventListener('click', () => setzeZustand({ k: '', q: '', stufe: '', geraet: '', tag: '' }));
let warte = null;
@@ -317,6 +333,24 @@
window.addEventListener('popstate', laden_);
/* ---------- Kategoriewahl ------------------------------------------------ */
function setzeKategorie(schluessel) {
for (const b of document.querySelectorAll('.katwahl__knopf')) {
const an = b.dataset.kategorie === schluessel;
if (an) b.dataset.an = 'ja'; else delete b.dataset.an;
b.setAttribute('aria-checked', an ? 'true' : 'false');
}
const hinweis = $('n-kat-hinweis');
if (hinweis) {
const k = aufbau?.kategorien.find((x) => x.schluessel === schluessel);
hinweis.textContent = k ? k.beschreibung : 'Noch keine Kategorie gewählt.';
}
}
const gewaehlteKategorie = () =>
document.querySelector('.katwahl__knopf[data-an="ja"]')?.dataset.kategorie || '';
/* ---------- Formular ------------------------------------------------------ */
function formularOeffnen(eintrag) {
@@ -335,7 +369,7 @@
nicht bewusst aendert, merkt es nicht. Vorbelegt wird nur, wenn
man aus EINER Kategorie heraus hochlaedt -- dann ist es eine
informierte Annahme, keine geratene. */
$('n-kategorie').value = eintrag ? eintrag.kategorie : (zustand().k || '');
setzeKategorie(eintrag ? eintrag.kategorie : (zustand().k || ''));
$('n-stufe').value = eintrag ? eintrag.stufe : 'einsteiger';
$('n-geraet').value = eintrag ? eintrag.geraet : 'egal';
$('n-tags').value = eintrag ? eintrag.tags.join(', ') : '';
@@ -366,25 +400,32 @@
f.append(o);
}
};
/* Nach Welten gruppiert -- dieselbe Gliederung wie auf der Seite.
Zwanzig Eintraege in einer flachen Liste findet man nicht. */
/* Nach Welten gruppiert -- dieselbe Gliederung und dieselben Farben
wie die Kacheln auf der Seite. Man waehlt aus demselben Regal, in
dem man sonst stoebert. */
const kf = $('n-kategorie');
kf.textContent = '';
const platzhalter = document.createElement('option');
platzhalter.value = '';
platzhalter.textContent = '— Kategorie wählen —';
platzhalter.disabled = true;
kf.append(platzhalter);
for (const welt of aufbau.welten) {
const gruppe = document.createElement('optgroup');
gruppe.label = welt.name;
for (const k of aufbau.kategorien.filter((x) => x.welt === welt.schluessel)) {
const o = document.createElement('option');
o.value = k.schluessel;
o.textContent = k.name;
gruppe.append(o);
const kats = aufbau.kategorien.filter((x) => x.welt === welt.schluessel);
if (!kats.length) continue;
const gruppe = el('div', 'katwahl__welt');
gruppe.dataset.welt = welt.schluessel;
gruppe.append(el('p', 'katwahl__weltname', welt.name));
const reihe = el('div', 'katwahl__reihe');
for (const k of kats) {
const b = el('button', 'katwahl__knopf');
b.type = 'button';
b.dataset.kategorie = k.schluessel;
b.setAttribute('role', 'radio');
b.setAttribute('aria-checked', 'false');
b.title = k.beschreibung;
b.append(symbol(k.symbol, 'katwahl__symbol'));
b.append(el('span', 'katwahl__name', k.name));
b.addEventListener('click', () => setzeKategorie(k.schluessel));
reihe.append(b);
}
if (gruppe.children.length) kf.append(gruppe);
gruppe.append(reihe);
kf.append(gruppe);
}
setz('n-stufe', Object.entries(aufbau.stufen));
setz('n-geraet', Object.entries(aufbau.geraete));
@@ -466,7 +507,7 @@
const angaben = {
titel: $('n-titel').value.trim(),
beschreibung: $('n-beschreibung').value.trim(),
kategorie: $('n-kategorie').value,
kategorie: gewaehlteKategorie(),
stufe: $('n-stufe').value,
geraet: $('n-geraet').value,
tags: $('n-tags').value,
@@ -476,7 +517,8 @@
if (angaben.titel.length < 3) { $('n-fehler').textContent = 'Der Titel fehlt.'; return; }
if (!angaben.kategorie) {
$('n-fehler').textContent = 'Wähle die Kategorie aus, in der die Anleitung stehen soll.';
$('n-kategorie').focus();
$('n-kategorie').scrollIntoView({ block: 'center', behavior: 'smooth' });
$('n-kategorie').querySelector('.katwahl__knopf')?.focus();
return;
}