Der Installieren-Knopf war nie zu sehen — jetzt schon

Filipe, 22.09.2026: "und nochmal, ich will einen installieren button.
damit die leute es viel einfacher haben die seite auf dem pc oder auf
dem handy zu installieren!!! bitte das hab ich auch schon paar mal
gefragt. mach das es ist seeeeeeeehr wichtig."

Er hatte recht, und der Knopf war seit dem 20.09. gebaut. Er konnte
nur nie erscheinen. ZWEI URSACHEN, beide ausserhalb dessen, was
geprueft wurde:

1. DER SERVICE WORKER LIEF NIE. Er wurde ausschliesslich in glocke.js
   angemeldet -- also erst, wenn jemand Benachrichtigungen ERLAUBT.
   Auf Filipes Rechner stehen sie auf "Vom Browser blockiert"; das
   steht sogar in seiner Kopfleiste auf dem Bildschirmfoto von heute
   frueh. Ohne Service Worker macht Chrome kein Installationsangebot.

2. UND AUCH MIT WAERE ES NICHT GEGANGEN: Chrome verlangt einen Service
   Worker MIT `fetch`-Zuhoerer. Dieser hatte keinen.

Ohne Angebot kommt `beforeinstallprompt` nie, und der Knopf bleibt
`hidden`. Fuer immer.

WAS SICH GEAENDERT HAT

- Der Service Worker wird jetzt auf JEDER Seite angemeldet, unabhaengig
  von Benachrichtigungen. Das gehoert zum Installieren, nicht zur
  Glocke.
- sw.js bekommt einen `fetch`-Zuhoerer, der NICHTS ablegt. Das
  Versprechen im Kopf der Datei ("bewusst ohne Zwischenspeicher, der
  Workspace liegt hinter einer Anmeldung") bleibt damit wortwoertlich
  gueltig: Er reicht Seitenaufrufe durch und baut nur dann selbst eine
  Antwort, wenn das Netz weg ist. Keine Serverantwort wird aufgehoben.
- Der Knopf zeigt sich, sobald der Browser installieren KANN, statt
  erst nach dem Angebot. Geprueft wird die Faehigkeit
  (`'onbeforeinstallprompt' in window`), nicht der Name des Browsers.
- DIE ANMELDEWAND BEKOMMT IHN AUCH. Sie hat keine Kopfleiste und
  deshalb bisher gar kein Angebot -- dabei ist sie die Seite, auf der
  jeder zuerst landet. Genau die Leute, um die es Filipe geht.
- Dafuer steht der Knopf jetzt in einer eigenen Datei
  (assets/js/installieren.js) statt in kopf.js, das die Wand nicht
  laedt. Und sein Stil in gate.css statt in module.css -- vierter Fall
  derselben Art nach .knopf-still, dem Schalter und .feld-hinweis.
- Ohne `nachfrage.js` (also auf der Wand) erklaert er den Weg als
  Absatz unter sich. Der erste Entwurf rief `window.frageNach?.()`
  auf: Auf dem iPhone steht der Knopf dort von Anfang an da, und er
  haette beim Antippen stumm nichts getan.
- Der Aufruf haengt nicht mehr an der Reihenfolge der Skriptzeilen.
  chat.html und leistung.html laden kopf.js OHNE `defer`, dort waere
  installieren.js immer zu spaet gekommen -- zwei von 37 Seiten ohne
  Knopf, ohne Meldung.
- Chromes Manifest-Warnung zum `share_target` behoben (enctype).

WARUM DIE PRUEFUNG DAS NICHT GEFUNDEN HAT -- und was jetzt anders ist

Sie war gruen, die ganze Zeit. Sie hat `beforeinstallprompt` SELBST
zugestellt und gemessen, ob der Knopf darauf reagiert. Die eine Frage,
auf die es ankam -- "bietet der Browser es ueberhaupt an?" -- hat sie
nie gestellt.

Jetzt fragt sie Chrome direkt (`Page.getInstallabilityErrors`, sein
eigenes Urteil) und misst die Voraussetzungen statt der Reaktion:
laeuft ein Service Worker, OBWOHL Benachrichtigungen auf "denied"
stehen; hat sw.js einen fetch-Zuhoerer; legt er wirklich nichts ab;
steht der Knopf auf der Wand und tut er dort auch etwas.

Gemessen, mit Benachrichtigungen auf "denied":
  Service Worker: activated
  Chromes Urteil: kein einziges Hindernis
  Manifest:       nichts beanstandet
  Knopf:          124x45 px, sichtbar, genau einer

pruef-installieren 29/0 (von 14). Dazu gruen: pruef-css-klassen,
pruef-struktur, pruef-crew-adresse, pruef-start-ansicht.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-22 14:16:03 +02:00
co-authored by Claude Opus 5
parent 774eeee3e5
commit a8e49496ad
45 changed files with 3008 additions and 2457 deletions
+21 -130
View File
@@ -1037,139 +1037,30 @@
const kopfRechts = document.querySelector('.kopfleiste__rechts');
if (kopfRechts) aufbauSuche(kopfRechts);
if (kopfRechts) aufbauChat(kopfRechts);
if (kopfRechts) aufbauInstallieren(kopfRechts);
/* DER AUFRUF DARF NICHT VON DER REIHENFOLGE DER SKRIPTZEILEN
ABHAENGEN. Auf chat.html und leistung.html steht `kopf.js` OHNE
`defer` -- ein solches Skript laeuft sofort beim Einlesen, also
immer vor jedem aufgeschobenen. `installieren.js` gaebe es dort
zu diesem Zeitpunkt noch nicht, und `?.()` verschluckt das
lautlos: kein Knopf, keine Meldung, auf genau zwei von
siebenunddreissig Seiten.
/* =====================================================================
DIE SEITE ALS APP INSTALLIEREN (20.09.2026)
Filipe: „kannst du da dazwischen in der starseite oben, ein button
reinsetzen damit die leute die seite installieren koennen auf dem
handy oder pc. das soll auch verschwinden wenn die leute es
installiert haben."
WARUM ES DEN KNOPF BRAUCHT: Das Manifest ist seit Langem
vollstaendig -- die App LIESS sich installieren. Nur bot das
ausschliesslich der Browser selbst an, versteckt in seinem
Dreipunktemenue. Wer es nicht sucht, findet es nie. Und ohne
installierte App gibt es auf dem Handy keine verlaesslichen
Benachrichtigungen; genau daran hing im September das Telefonieren.
DREI ZUSTAENDE, UND JEDER BRAUCHT EINE ANDERE ANTWORT:
1. SCHON INSTALLIERT -> gar kein Knopf. Ein Angebot fuer etwas,
das man hat, ist Rauschen. Erkannt an `display-mode:
standalone` (und an Safaris eigenem `navigator.standalone`).
2. DER BROWSER BIETET ES AN (Chrome, Edge, Android) -> der Knopf
fragt ihn. `beforeinstallprompt` kommt EINMAL und muss
aufgehoben werden; wer ihn nicht festhaelt, kann spaeter
nicht mehr fragen.
3. SAFARI AUF DEM iPHONE -> dort gibt es `beforeinstallprompt`
NICHT, und es wird ihn nicht geben. Apple laesst nur den Weg
ueber „Teilen -> Zum Home-Bildschirm". Ein Knopf, der dort
nichts tut, waere schlimmer als keiner -- also sagt er in
diesem Fall, was zu tun ist, statt es zu versuchen.
WARUM IN DER KOPFLEISTE UND NICHT NUR AUF DER STARTSEITE: Die
Leiste steht auf allen 32 Seiten. Wer den Knopf einmal uebersieht,
sieht ihn beim naechsten Mal wieder -- und er verschwindet ohnehin,
sobald er ueberfluessig ist.
===================================================================== */
function aufbauInstallieren(wohin) {
const el = (tag, klasse, text) => {
const k = document.createElement(tag);
if (klasse) k.className = klasse;
if (text !== undefined) k.textContent = text;
return k;
};
/* ---- Zustand 1: schon installiert -> nichts tun --------------- */
const schonDrin = () =>
(window.matchMedia && window.matchMedia('(display-mode: standalone)').matches)
|| window.navigator.standalone === true;
if (schonDrin()) return;
/* Apple: kein `beforeinstallprompt`, aber ein Weg. */
const istApple = /iphone|ipad|ipod/i.test(navigator.userAgent)
|| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
let angebot = null; // der aufgehobene Browser-Vorschlag
const knopf = el('button', 'install-knopf');
knopf.type = 'button';
knopf.hidden = true;
knopf.title = 'Diese Seite als App installieren';
/* DAS WORT VERSCHWINDET AM HANDY (siehe CSS) -- der Satz fuer
Vorleseprogramme nicht. Ein Knopf, der dort nur aus einem
Pfeil besteht, heisst sonst "Schaltflaeche". */
knopf.setAttribute('aria-label', 'Diese Seite als App installieren');
const zeichen = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
zeichen.setAttribute('viewBox', '0 0 24 24');
zeichen.setAttribute('class', 'install-knopf__zeichen');
zeichen.setAttribute('aria-hidden', 'true');
zeichen.innerHTML = '<path d="M12 3.5v11M8 11l4 4 4-4"/><path d="M5 18.5h14"/>';
knopf.append(zeichen, el('span', 'install-knopf__text', 'Installieren'));
knopf.addEventListener('click', async () => {
if (angebot) {
/* Zustand 2: den Browser fragen. Die Antwort interessiert uns
nur insofern, als ein angenommener Vorschlag den Knopf
ueberfluessig macht -- ein abgelehnter nicht: Vielleicht
ueberlegt man es sich morgen anders. */
knopf.disabled = true;
try {
angebot.prompt();
const { outcome } = await angebot.userChoice;
if (outcome === 'accepted') knopf.remove();
} catch { /* dann eben nicht */ }
angebot = null;
knopf.disabled = false;
return;
}
/* Zustand 3: erklaeren statt versuchen. */
window.frageNach?.({
titel: 'Als App auf dem Home-Bildschirm',
was: istApple
? 'Tippe unten in Safari auf das Teilen-Zeichen (Viereck mit Pfeil nach oben), '
+ 'dann auf „Zum Home-Bildschirm".'
: 'Öffne das Menü deines Browsers (die drei Punkte) und wähle dort '
+ '„App installieren" oder „Zum Startbildschirm hinzufügen".',
bleibt: 'Danach liegt die Seite wie eine App auf deinem Gerät – und '
+ 'Anrufe und Nachrichten kommen zuverlässiger an.',
ja: 'Alles klar',
nein: null,
});
});
wohin.prepend(knopf);
/* Der Browser meldet sich, wenn er es anbieten kann. */
window.addEventListener('beforeinstallprompt', (e) => {
/* Ohne `preventDefault` zeigt der Browser seine eigene Leiste --
zwei Angebote fuer dieselbe Sache sind eines zu viel. */
e.preventDefault();
angebot = e;
knopf.hidden = false;
});
/* Auf Apple gibt es diese Meldung nie -- dort steht der Knopf von
Anfang an da und erklaert den Weg. */
if (istApple) knopf.hidden = false;
/* Und wenn es geklappt hat, ist er fertig. */
window.addEventListener('appinstalled', () => knopf.remove());
/* Manche Browser melden `appinstalled` nicht -- dann faellt der
Knopf beim naechsten Oeffnen ueber `schonDrin()` weg. Und wer die
App waehrend der Sitzung installiert und zurueckwechselt, merkt
es hier: */
try {
window.matchMedia('(display-mode: standalone)')
.addEventListener('change', (m) => { if (m.matches) knopf.remove(); });
} catch { /* aeltere Browser koennen das nicht -- kein Grund zu scheitern */ }
Die Zeile in den HTML-Dateien zu verschieben haette es heute
behoben und beim naechsten neuen Skript wieder gebrochen. Also
lieber gar keine Reihenfolge voraussetzen. */
if (kopfRechts) {
if (window.installKnopfBauen) window.installKnopfBauen(kopfRechts);
else window.addEventListener('load',
() => window.installKnopfBauen?.(kopfRechts), { once: true });
}
/* Der Installieren-Knopf steht seit dem 22.09.2026 in einer eigenen
Datei: assets/js/installieren.js. Grund war nicht die Groesse,
sondern die Anmeldewand: Sie laedt kopf.js nicht (sie hat keine
Kopfleiste) -- und sie ist die Seite, auf der jeder zuerst landet.
Ein Angebot zum Installieren, das genau dort fehlt, erreicht die
Leute nicht, um die es Filipe geht. */
/* ---------- Der Chat-Knopf mit Zähler -----------------------------------
Wunsch Filipe, 06.09.2026: *"die andere person bekommt eine