consent.js posts the consent form in the background, the server answers 204 with the cookie, and the script swaps in the embeds and the legal line from a fresh copy of the page. Embed hosts are in frame-src from the start, the content address still only after consent. Without JavaScript the form keeps working with a reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
3.9 KiB
JavaScript
92 lines
3.9 KiB
JavaScript
// Einwilligung ohne Neuladen (ADR 115).
|
|
//
|
|
// Ohne dieses Skript schicken der Hinweis und der Knopf am eingebetteten Inhalt ein Formular ab,
|
|
// der Server setzt das Cookie und leitet auf dieselbe Seite zurück - die Seite lädt neu. Mit dem
|
|
// Skript geht dasselbe Formular im Hintergrund an denselben Endpunkt; die Kopfzeile
|
|
// X-Kant-Consent sagt dem Server, dass keine Weiterleitung nötig ist. Danach holt das Skript die
|
|
// Seite noch einmal und übernimmt daraus nur, was sich durch die Entscheidung ändert: die
|
|
// eingebetteten Inhalte und die Zeile „Rechtliches“. Die Adresse eines Inhalts steht so weiterhin
|
|
// erst nach der Einwilligung im Dokument.
|
|
//
|
|
// Geht dabei etwas schief, schickt das Skript das Formular ganz normal ab: lieber einmal neu
|
|
// laden als eine Entscheidung, die nicht ankommt.
|
|
(() => {
|
|
const forms = document.querySelectorAll('.consent-bar form, .embed-blocked form');
|
|
if (forms.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const replaceFrom = async () => {
|
|
const response = await fetch(location.href, { credentials: 'same-origin', headers: { Accept: 'text/html' } });
|
|
if (!response.ok) {
|
|
throw new Error('Seite nicht geladen');
|
|
}
|
|
const fresh = new DOMParser().parseFromString(await response.text(), 'text/html');
|
|
for (const blocked of document.querySelectorAll('.embed-blocked[id]')) {
|
|
const next = fresh.getElementById(blocked.id);
|
|
if (next && !next.classList.contains('embed-blocked')) {
|
|
blocked.replaceWith(document.importNode(next, true));
|
|
}
|
|
}
|
|
for (const legal of document.querySelectorAll('.block-legal[id]')) {
|
|
const next = fresh.getElementById(legal.id);
|
|
if (next) {
|
|
legal.replaceWith(document.importNode(next, true));
|
|
}
|
|
}
|
|
};
|
|
|
|
for (const form of forms) {
|
|
form.addEventListener('submit', async (event) => {
|
|
event.preventDefault();
|
|
const button = event.submitter;
|
|
const body = new FormData(form, button);
|
|
const placeholder = form.closest('.embed-blocked');
|
|
for (const control of form.querySelectorAll('button')) {
|
|
control.disabled = true;
|
|
}
|
|
try {
|
|
const saved = await fetch(form.action, { method: 'POST', body, credentials: 'same-origin', headers: { 'X-Kant-Consent': '1' } });
|
|
if (!saved.ok) {
|
|
throw new Error('Entscheidung nicht gespeichert');
|
|
}
|
|
// Wer etwas erlaubt hat, bekommt die Inhalte jetzt; wer abgelehnt hat, braucht keinen
|
|
// zweiten Abruf - es ändert sich nichts außer dem Hinweis.
|
|
if (body.get('choice') !== 'none' && document.querySelector('.embed-blocked')) {
|
|
await replaceFrom();
|
|
}
|
|
document.querySelector('.consent-bar')?.remove();
|
|
if (placeholder) {
|
|
// Der Knopf ist mit dem Platzhalter verschwunden; der Fokus geht an den Inhalt, der an
|
|
// seiner Stelle steht.
|
|
const frame = document.getElementById(placeholder.id)?.querySelector('iframe');
|
|
frame?.focus();
|
|
} else {
|
|
// Entschieden im Hinweis: der Fokus beginnt wieder am Seitenanfang, wie nach dem
|
|
// Neuladen - der nächste Tab führt zu „Zum Inhalt springen“.
|
|
document.getElementById('seitenanfang')?.focus({ preventScroll: true });
|
|
}
|
|
} catch {
|
|
// Rückfall: das Formular normal absenden, samt gedrücktem Knopf.
|
|
for (const control of form.querySelectorAll('button')) {
|
|
control.disabled = false;
|
|
}
|
|
withChoice(form, button).submit();
|
|
}
|
|
});
|
|
}
|
|
|
|
// form.submit() schickt den gedrückten Knopf nicht mit; sein Wert wandert als verstecktes Feld
|
|
// ins Formular, damit der Rückfall dieselbe Entscheidung sendet.
|
|
function withChoice(form, button) {
|
|
if (button?.name) {
|
|
const field = document.createElement('input');
|
|
field.type = 'hidden';
|
|
field.name = button.name;
|
|
field.value = button.value;
|
|
form.append(field);
|
|
}
|
|
return form;
|
|
}
|
|
})();
|