Stephan Böni - SVG-Icons

SVG-Icons

SVG-Icons

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.

SVG <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <path d="M31.385 3.301c-0.25-0.25-0.655-0.25-0.905 0l-21.787 21.787c-0.25 0.25-0.655 0.25-0.905 0l-6.204-6.204c-0.25-0.25-0.655-0.25-0.905 0l-0.226 0.226c-0.25 0.25-0.25 0.655 0 0.905l7.004 7.004c0 0 0.001 0 0.001 0v0c0-0 0.001-0 0.001 0l0.33 0.33c0.25 0.25 0.655 0.25 0.905 0l22.917-22.917c0.25-0.25 0.25-0.655 0-0.905l-0.226-0.226z"></path> </svg>

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.

default.js // Load Icons async function loadIcons() { const Icons = await loadModule('Icons'); Icons.load(); }

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.js export 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); }); } } } } } }

icons.js herunterladen

Den Server-Pfad "/includes/icons/" wirst du wohl noch anpassen müssen.

Icons stylen

Der ganze Rest ist nun noch etwas CSS, damit das Icon schön aussieht.

CSS .icon-check { display: inline-flex; align-items: center; justify-content: center; /* check icon */ svg { height: 2.4rem; width: 2.4rem; pointer-events: none; path { fill: var(--default-color); } } /* placeholder while loading */ &:not(:has(svg))::before { content: ''; height: 2.4rem; width: 2.4rem; } }

Damit die Darstellung ruhig bleibt, definieren wir einen Platzhalter, solange das Icon noch nicht geladen ist.

Dran bleiben

Du hast es geschafft. Abonniere meine Benachrichtigungen, um weitere News und Anleitungen von mir zu erhalten.

Feed einbinden