diff --git a/src/components/slides/javascript/09-web-api/files.astro b/src/components/slides/javascript/09-web-api/files.astro
new file mode 100644
index 0000000..7d6becd
--- /dev/null
+++ b/src/components/slides/javascript/09-web-api/files.astro
@@ -0,0 +1,170 @@
+ Als wir über Eingabemöglichkeiten in HTML mit dem Sobald der Nutzer eine Datei ausgewählt hat, können wir im dazugehörigen change-Eventlistener auf diese Datei und deren Inhalt zugreifen. Wir müssen also nicht unbedingt den Inhalt an einen Server senden. Es ist aber auch möglich, mit der normalen DOM-API auf die Dateien zuzugreifen. Sehen wir uns einmal an, was wir beim Das Objekt das wir hier erhalten stammt von der besonderen Klasse FileList. Das Objekt verhält sich im weitesten Sinne wie ein Array. Also ist auch der Zugriff mittels der Array-Syntax auf einzelne Elemente möglich. Sehen wir uns mal den Inhalt genauer an. Innerhalb einer FileList befinden sich File Objekte. Dort befinden sich ein paar Informationen zur ausgewählten Datei. Im Log finden wir folgendes: Neben den Allgemeinen Informationen wie Dateiname, Dateigröße oder das letzte Änderungsdatum, bietet uns die darunter liegende Klasse "Blob". Diese Klasse bietet uns ein paar Methoden um den Inhalt aus der Datei zu erhalten. Für einfache Text-Dateien gibt es hierfür die Convenience-Funktion Das Ergebnis der Funktion resultiert nach dem Bei anderen Dateien (z.B. Bilder) fällt ein wenig mehr Arbeit an. Insgesamt ist es in JavaScript nicht notwendig den Inhalt aus einer Textdatei zu extrahieren; Bei einem Versand mit dem Form-Element sorgt der Browser dafür, dass der Inhalt korrekt übertragen wird. Wollen wir aber den Inhalt der Datei noch in der Web-Ansicht editiert, betrachtet oder sonst in irgendeiner Art und Weise modifizieren, müssen wir - zumindest für Binär-Dateien wie Bildern - noch der Inhalt extrahiert werden. Im Folgenden Beispiel anhand eines Bildes, betrachten wir 2 Möglichkeiten um den Inhalt vor dem Absenden anzuschauen. Das Bild zur Vorschau darstellen Was passierte: Variante 2: Den eigentlichen Datei-Inhalt "parsen" und die Binär-Daten zur Preview verwenden. Hier ist etwas "mehr" Arbeit zu verrichten. Vorteil mit dieser Art und Weise ist aber, dass wir den Datei-inhalt direkt verfügbar haben und modifizieren können (Zumindest mit dem konkreten Wissen wie man das macht). Aus dieser Position heraus haben wir alle Bildinformationen vorhanden und können mit diesen Arbeiten / modifizieren. Wie wir diese Informationen modifizieren um das Bild zu modifizieren ist außerhalb des Scopes der Vorlesung.
+ Mit der History API können wir per JavaScript mit der Browser-History
+ interagieren.
+
+ Während sich der User durch verschiedene Dokumente klickt, wird jeder
+ Seitenbesuch in die History gelegt.
+
+ Durch die Buttons neben der URL-Bar interagiert der Nutzer mit der
+ Browser-History und springt darin vor und zurück.
+
+ Das
+ Es bietet einige Funktionen um zu anderen Seiten zu navigieren und die
+ bisherige history zu modifizieren.
+
+ Mit dieser Funktion springen wir in der Browser-History einen Eintrag aus
+ dem aktuellen Tab zurück.
+
+ Mit dieser Funktion springt der Browser in der History um einen Eintrag
+ "nach vorne", sofern in der History nach vorne ein Eintrag vorhanden ist.
+
+ Mittels go können wir mehrere Einträge in der Browser-History
+ überspringen.
+
+ Mit dem delta Parameter können wir die Anzahl der Einträge bestimmen.
+ (Es sind auch negative Zahlen möglich)
+
+ Die Funktionen
+ Dieser Umstand ist wichtig, wenn wir sogenannte Single State Applications entwickeln. Wir müssen dann in der Lage sein eine Navigation abzubrechen
+ und den Zustand in der UI zu aktualisieren.
+
+ Für den normalen Webentwickler ist dieser Umstand "weniger wichtig",
+ moderne Bibliotheken und Frameworks kapseln die Logik komplett ab.
+
+ Aber sehen wir uns das
+ Dieses Event ist für den Umstand wichtig, wenn der Nutzer mittels der
+ Browser-Navigation die Seite ändert.
+
+ Für den Fall das der User mit der Seite interagiert und eine Navigation
+ auslöst, können wir einfach das Event abfangen.
+
+ Mit
+ Der Browser wird nicht automatisch die neue URL laden, sondern dann wenn
+ es notwendig ist (z.B. wenn der Browser neu startet.). Wir können aber die
+ neue "gepushte" URL mit
+ Wir können nur URL's auf der gleichen Origin angeben, ansonsten wirft die
+ Funktion einen Error.
+
+ Ähnlich zu
+ Anders aber ist aber, dass der aktuelle (oberste) History-Eintrag von
+ dieser Funktion überschrieben wird.
+
+ Besonders nützlich ist diese Funktionalität, wenn der User auf einer Seite
+ die URL mehrmals durch Aktionen ändert, die aber keinen Reload der Seite
+ triggern.
+ Gängige Beispiele hierfür sind:
+ Die URL-Klasse ist hilfreiche um eine Strukturierte URL zu generieren.
+
+ Sie bietet viele Funktionen um eine URL durch verschiedene Teile zu
+ erweitern (Query-Parameter zum Beispiel)
+
+ Das URL-Objekt lässt sich folgendermaßen aus dem Konstruktor erstellen
+ Diese URL kann z.B. auch für die History-API verwendet werden.
+ Das resultierende URL Objekt hat nun Möglichkeiten auf bestimmte Teile
+ zuzugreifen und zu modifieren, sowie die komplette URL zu generieren.
+
+ Wenn wir URL's modifizieren oder generieren müssen, sollten wir die
+ gegebene URL-Klasse verwenden, anstatt einen selbst-gebauten String zu
+ kreiieren.
+
+ Die URL-Klasse hat einen entscheidenden Vorteil: Potenziell "kritische"
+ Zeichen werden von der Klasse korrekt behandelt.
+ Files API
+ <input />
Element betrachtet haben, haben wir auch den type="file"
kennen gelernt.change-event
so erhalten.
+
+ const input = document.getElementById('file-input');
+
+ input.addEventListener('change', onFile, false);
+
+ function onFile (event) {
+ // Alle selektierten Dateien befinden sich im Element in "files"
+ const selected = event.target.files;
+ console.log(selected);
+ }
+
+
+ function onFile (event) {
+ const file = event.target.files[0];
+
+ console.log(file);
+ }
+
+
+ File {
+ lastModified: 1730637507228,
+ name: "datei.png",
+ size: 2583226,
+ type: "image/png"
+ }
+
.text()
. Diese Funktion ist async, sprich muss mit einem await
versehen werden.await
den Textinhalt der Datei.
+
+ function onFile(event) {
+ const file = event.target.files[0];
+
+ const previewElement = document
+ .getElementById('preview');
+
+ if (file) {
+ const url = URL.createObjectURL(file);
+
+ preview.src = url;
+ preview.onload = function () {
+ URL.revokeObjectURL(url);
+ }
+ }
+ }
+
+
+ File
vorhanden istURL.createObjectURL(file);
erstellen wir einen internen Browserlink
+
+ src
anhängenonload
Funktion darauf reagieren.URL.revokeObjectURL(url);
aufrufen.
+
+ function onChange(event) {
+ const file = event.target.files[0];
+ const preview = document.getElementById('preview');
+
+ function parseImage(e) {
+ // ...
+ }
+
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = parseImage;
+ reader.readAsArrayBuffer(file);
+ }
+ }
+
+
+ FileReader
-Instanz anreader
erwartet eine Funktion für ein onload
Event, wir weisen ihr die parseImage
-Funktion zureadAsArrayBuffer
der FileReader-Instanz auf. Bei Erfolg ruft diese die Funktion parseImage
auf.
+
+ function parseImage(e) {
+ const arrayBuffer = e.target.result;
+ const uint8Array = new Uint8Array(arrayBuffer);
+
+ const base64 = btoa(uint8Array.reduce((data, byte) =>
+ data + String,fromCharCode(byte), ''
+ );
+
+ const mimteType = file.type || 'image/jpeg';
+ const dataUrl = `data:${mimeType};base64,${base64}`;
+
+ preview.src = dataUrl;
+ }
+
+
+ readAsArrayBuffer
erhalten wir in unserer Funktion im Event. Dieses nehmen wir und konvertieren es in ein Uint8Arraysrc
-Attribut des img
-ElementesHistory API
+ history
Objekt ist im Browser im globalen Scope verfügbar.
+ history.back()
+ history.forward()
+ history.go(delta)
+ back, forward & go
sind asynchron und lösen popstate
Events aus.
+ popstate
Event kurz etwas näher an:
+
+
+ // Das Event kommt am window an
+ window.addEventListener('popstate', (event) => {
+ console.log(`location: ${document.location}`);
+ // in event.state stehen ggf. weitere infos
+ console.log(`state: ${JSON.stringify(event.state)}`);
+ })
+
+
+ // aTag ist nur ein generisches a-tag auf das ein User klickt
+ aTag.addEventListener('click', (event) => {
+ // Unterbricht die Event-Kette
+ // Verhindert dass der Browser die Seite wechselt
+ event.preventDefault();
+
+ // Ab hier können wir die UI aktualisieren
+ });
+
history.pushState(state, unused, url)
+ pushState
fügen wir einen neuen Eintrag in der Browser-History
+ hinzu.
+ history.go()
aufrufen.
+ history.replaceState(state, unused, url)
+ pushState
, modifiziiert diese Funktion die
+ Browser-History.
+
+
+ Die URL-Klasse
+
+
+ const baseUrl = "https://www.denis-ergin.de";
+
+ // Erstellt eine URL zu
+ // "https://www.denis-ergin.de/slides/javascript/00-intro"
+ const url = new URL("/slides/javascript/00-intro", baseUrl);
+
+ /**
+ * new URL(Pfad, BaseUrl);
+ */
+
+
+ const baseUrl = "https://www.denis-ergin.de";
+ const url = new URL("/slides/javascript/00-intro", baseUrl);
+
+ console.log(
+ url.host, // Hostname + ggf Port
+ url.hostname, // z.B. google
+ url.origin, // scheme + domain + port
+ url.pathname, // Pfad ("/...")
+ url.search, // Der Query-String (kommt nach dem "?")
+ url.protocol, // Protokoll (":" inbegriffen)
+ );
+
+ Mit der Storage-API haben wir eine einfache aber wirkungsvolle Art, um Informationen zu speichern und später wieder abzufragen. +
++ Dafür stehen uns 2 "dedizierte" Speicher-Orte zur Verfügung: Der sessionStorage und der localStorage. +
++ Beide haben die gleichen Funktionen um Inhalte zu speichern, nur die "Speicherdauer" variiert. +
+
+ Während wir im localStorage
Inhalte auf "unbestimmte" Zeit speichern können, werden Daten im sessionStorage
gelöscht, wenn der letzte Tab der Website geschlossen wird, auf dem die Daten gespeichert wurden.
+
+ Da beide Arten die gleiche API besitzen, betrachten wir nur die langfristige Variante zum Speichern von Daten. +
++ Daten im storage werden anhand eines Key-Value-Paares gespeichert. Das lässt uns mit der Gefahr im Fall der Fälle ungewollt Daten zu überschreiben. Einmal überschrieben können wir auch nicht mehr auf die Daten davor zugreifen. +
+
+ Wichtig ist zu beachten, dass nur Strings gespeichert werden können. Wollen wir also JSON-Formattierte Daten speichern wollen, müssen wir diese vorher mit JSON.stringify()
zu einem String umwandeln.
+
+ const toSave = "Hallo TINF24BX!";
+ const key = 'greeting';
+
+ localStorage.setItem(key, toSave);
+
+ + Wir können die Daten aus dem storage mithilfe des Key's auch wieder abholen: +
+
+ const data = localStorage.getItem('key');
+
+ console.log(data);
+
+
+ Auch hier gilt wieder: Wenn wir strukturierte Dateien im JSON-Format abgespeichert haben, so müssen die Daten auch wieder mit JSON.parse()
umformattiert werden um diese zu nutzen.
+
Sollten wir Daten aus dem Storage löschen wollen, geht das ganz einfach mit dem key und der Funktion localStorage.removeItem(key)
.
+ const key = 'greeting';
+
+ let data = localStorage.getItem(key);
+ console.log(data); // Wir erwarten hier etwas...
+
+ localStorage.removeItem(key);
+ data = localStorage.getItem(key);
+
+ console.log(data); // undefined...
+
+ Wenn wir den gesamten Storage löschen möchten, gibt es die Funktion localStorage.clear()
. Daraufhin werden alle gespeicherten Key-Value-Paare gelöscht.