Zur Anzeige eines Icons soll nur genau den für dieses Icon erforderliche SVG-Code geladen werden. Oft werden jedoch dieselben Icons mehrfach benötigt, was unnötige Redundanzen der vom Server gelieferten Daten erfordern und damit die Performance beeinträchtigen würde. Daher ist es besser, die Icons beim ersten Laden im Session Storage abzulegen und ab dem zweiten Bedarf von dort zu holen.
Wir gehen daher davon aus, dass initial nur ein leeres <span class="icon-xyz"> gerendert wird, um darin das SVG-Icon per Javascript aus dem lokalen Session Storage zu laden. Und nur falls es dort nicht vorhanden ist, wird eine Server-Abfrage gemacht, also genau einmal pro Session.
Icons bereitstellen
Die Icons werden als minimale SVG-Dateien auf dem Server in einem Ordner gesammelt. Nehmen wir als Beispiel ein Haken-Icon und nennen es "check.svg". Der SVG-Code - also der Inhalt der Datei - ist trivial.
Man beachte, dass keine Grössenangaben (width und height) und ebenso keine Farben (fill) im SVG-Code enthalten sind. Die Grössen und Farben werden über das CSS definiert.
In dieser Art ist also für jedes Icon eine SVG-Datei zu hinterlegen.
Icons laden und einbinden
Wie bereits in der Einleitung erwähnt, ist im HTML-Code bei jedem Element, wo das Haken-Icon eingefügt werden soll, die CSS-Klasse icon-check zu ergänzen. Das ist HTML-seitg bereits alles.
Die ganze Magie, die das check.svg abholt und einbindet erfolgt über Javascript. Zuerst definieren wir in der Javascript-Datei, die in jeder HTML-Seite geladen wird, einen Observer. Dieser überwacht alle DOM-Mutationen.
default.js// Do on DOM Mutation
const targetNode = document.body;
const config = { attributes: true, childList: true, subtree: true };
const callback = (mutationList, observer) => {
loadIcons();
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
Immer wenn also eine Seite oder einen Teilbereich davon geladen wird, erkennt dies der Observer und führt die Funktion loadIcons(); aus. Auch diese Funktion hinterlegen wir in der Standard-Javascript-Datei.
Die eigentliche Magie lagern wir in ein separates Modul aus, das erst bei Bedarf per loadModule(); geladen wird.
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 Icons befindet sich also in ./modules/icons.js.
Jetzt kommen wir zum eigentlichen Kern. Wir suchen nach allen Elementen mit den CSS-Klassen [class*=icon-], die noch kein SVG-Element enthalten. Das wird auch unser icon-check finden. Dann schauen wir im Session Storage, ob dort der SVG-Code dazu vorhanden ist. Falls noch nicht, holen wir die SVG-Datei vom Server ab und legen den SVG-Code in den Session Storage. Schliesslich binden wir den SVG-Code im HTML ein.
icons.jsexport const Icons = class {
// load icons
load() {
// create svg element
function create(element, svg) {
const existingIcons = element.querySelectorAll(':scope > svg');
if (existingIcons) {
for (const icon of existingIcons) {
icon.remove();
}
}
let div = document.createElement('div');
div.innerHTML = svg.trim()
const icon = div.firstChild;
element.appendChild(icon);
}
// get svg values from server
async function getServerItem(className) {
const response = await fetch('/includes/icons/' + className.split('icon-')[1] + '.svg');
const svg = await response.text();
return [className, svg];
}
// find all missing icons
const icons = document.querySelectorAll('[class*=icon-]:not(:has(svg))');
for (const element of icons) {
const classList = element.className.split(' ');
for (const className of classList) {
if (className.includes('icon-')) {
// check for anchor element
let el = element;
if (element.querySelector('a')) {
el = element.querySelector('a');
}
// first try to get icon from session storage
if (sessionStorage.getItem(className) !== null) {
const svg = sessionStorage.getItem(className);
create(el, svg);
//console.log(className + ' from session storage');
// otherwise get icon from server
} else {
getServerItem(className).then(response => {
const iconName = response[0];
const svg = response[1];
create(el, svg);
//console.log(iconName + ' from server');
sessionStorage.setItem(iconName, svg);
});
}
}
}
}
}
}