Mit der Popover API lassen sich alle Sorten von Overlays nativ einbinden, also praktisch ohne Javascript. Unter dem Begriff Overlays (deutsch: Überlagerungen) werden alle Arten von Dialoge, Dropdowns, Toasts, Slide-Ins, Fly-Ins, usw. zusammengefasst. Popovers sind nicht-modal. Sie können aber pseudo-modal sein, indem sie als <dialog>-Element eingebunden werden.
Wir fokusieren uns jedoch in diesem Beitrag auf den Toast. Toasts sind Benachrichtigungen, die üblicherweise unten links oder rechts eingeblendet werden und die Bedienbarkeit der Seite nicht unterbrechen, also non-modal sind. Wir definieren dazu das benutzerdefinierte Element <toast-notification>.
Erfolgsmeldungen
Wir unterscheiden drei Arten von Erfolgsmeldungen: Alles gut (success), Okay mit Warnungen (warning), Abgebrochen mit Fehler (error). Bei Klick auf den untenstehenden Button wird je eine generiert. Success- und Warning-Meldungen werden nach 4 Sekunden automatisch ausgeblendet.
Ich gehe davon aus, dass eine Aktion jegliche Art von Erfolgsmeldungen zurückgeben kann, auch mehrere gleichzeitig. Diese werden als verschachteltes Array übrergeben. Nachfolgend das Beispiel, das bei obigem Button generiert wird.
Die default.js wird ihrerseits in jeder HTML-Seite möglichst weit unten aufgerufen, z.B. direkt vor dem </body>-Tag.
Modul einbinden
Damit die default.js nicht überbordet, sollten eher seltener benötigte Aktionen in Klassenmodule ausgelagert werden. Klassenmodule müssen bei Bedarf in der default.js importiert werden.
default.js// Load Module
async function loadModule(module) {
const file = './modules/' + module.charAt(0).toLowerCase() + module.slice(1) + '.js';
const mod = await import(file);
return new mod[module];
}
Ich sammle die Klassenmodule im Unterordner modules und nenne die Dateien wie die Klasse, aber mit Kleinbuchstaben beginnend. Die Klasse Toast befindet sich also in ./modules/toast.js.
Klasse Toast
Die Klasse Toast hat nur die eine Methode show, die ein oben genanntes Array mit Erfolgsmeldungen als Übergabewert erwartet.
./modules/toast.jsexport const Toast = class {
show(messages) {
// Define popover template
const template = '<p></p>\
<button onclick="this.parentElement.hidePopover();" class="icon">\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">\
<path d="m329.08-286.54-42.54-42.54L437.46-480 286.54-629.92l42.54-42.54L480-521.54l149.92-150.92 42.54 42.54L521.54-480l150.92 150.92-42.54 42.54L480-437.46 329.08-286.54Z"/>\
</svg>\
</button>';
// Loop over all messages
for (const msgType in messages) {
// Create toast
const popover = document.createElement('toast-notification');
popover.popover = 'manual';
popover.classList.add(msgType);
popover.classList.add('newest');
popover.innerHTML = template;
const content = popover.querySelector('p');
content.textContent = messages[msgType];
document.querySelector('main').appendChild(popover);
popover.showPopover();
popover.style.bottom = '16px';
const toastHeight = Math.round(popover.offsetHeight);
// Move other toasts up
const toasts = document.querySelectorAll("toast-notification");
for (const toast of toasts) {
if (toast.classList.contains('newest')) {
toast.classList.remove('newest');
} else {
const prevValue = toast.style.bottom.replace("px", "");
const newValue = parseInt(prevValue) + toastHeight + 16;
toast.style.bottom = `${newValue}px`;
}
}
// Remove the toast again after 4 seconds
setTimeout(() => {
if (!popover.classList.contains('error')) {
popover.hidePopover();
}
}, 4000);
// When the toast closes, move higher toasts down
popover.addEventListener("toggle", (event) => {
if (event.newState === "closed") {
popover.classList.add('removed')
const toastHeight = Math.round(popover.offsetHeight);
const toasts = document.querySelectorAll("toast-notification");
let down = 0;
for (let index = toasts.length - 1; index >= 0; index--) {
const toast = toasts[index];
if (toast.classList.contains('removed')) {
down = 1;
}
if (down == 1) {
const prevValue = toast.style.bottom.replace("px", "");
const newValue = parseInt(prevValue) - toastHeight - 16;
toast.style.bottom = `${newValue}px`;
}
}
popover.remove();
}
});
}
}
}
Du kannst die vollständige Toast-Klasse in einer optimierten Version hier herunterladen.
Das wirst du optisch wohl noch etwas auf dein CSS anpassen müssen.
Einzeler Toast senden
Das war eigentlich schon alles. Wenn du einfach einen einzelnen Toast senden willst, kannst du die nachfolgende Ergänzung in der default.js hinzufügen.