1
0
Fork 0
mirror of https://github.com/TheTaz25/denis.ergin.git synced 2025-07-06 12:08:49 +00:00

feat(slides): new: dom api

This commit is contained in:
Denis Ergin 2024-12-29 13:04:35 +01:00
parent ba2778991b
commit 5786a669e5
9 changed files with 639 additions and 0 deletions

View file

@ -0,0 +1,17 @@
<section>
<section>
<h2>Elemente aus dem DOM entfernen</h2>
</section>
<section>
<p>Elemente die wir in einer Variable haben, können wir mit <code>remove()</code> auf dem Element aus dem DOM entfernen.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
const toRemove = document.getElementById('test');
toRemove.remove();
</code></pre>
</section>
</section>

View file

@ -0,0 +1,327 @@
<section>
<section>
<h2>Events</h2>
</section>
<section>
<p>Events sind ein Zentraler Bestandteil aller Webapplikationen.</p>
<p>Mittels Events können wir auf Nutzerbasierte Aktionen (zum Beispiel Klicks auf Elemente oder Texteingaben) reagieren.</p>
<p>Es gibt eine Vielzahl an Events auf die man reagieren kann, die wichtigsten werden hier vorgestellt.</p>
</section>
<section>
<h3>onclick &amp; onmouseover</h3>
</section>
<section>
<p>Die wohl einfachsten Events: Sie werden dann aufgerufen, wenn ein User auf ein Element klickt (nicht nur auf buttons beschränkt) beziehungsweise mit der Maus über ein bestimmtes Element fährt.</p>
</section>
<section>
<p>Variante 1: Funktion überschreiben</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
document.body.onmouseover = function() {
console.log('hover!');
}
</code></pre>
<p>Jedes Element hat verschiedene Properties die mit dem Namen "on" beginnen. Diese Werte sind zunächst undefined, aber wenn wir diese mit einer eigenen Funktion belegen, wird diese Funktion zum jeweiligen Trigger ausgeführt.</p>
</section>
<section>
<p>Variante 2: <code>addEventListener</code></p>
<pre class="js"><code data-trim data-line-numbers is:raw>
function onBodyClick () {
// ToDo: Do Something on click!
}
// Wir sagen dem Element bei welchem Element eine Funktion ausgeführt wird (bei click, führe onBodyClick aus).
document.body.addEventListener('click', onBodyClick);
</code></pre>
<p>Mit <code>element.addEventListener</code> können wir einen (oder mehrere) Funktionen bei bestimmten Events ausführen.</p>
</section>
<section>
<p>Meine Persönliche Tendenz ist es, Variante 2 zu verwenden. Es gibt hierfür ein paar Vorteile, ob diese aber im Alltag oft Anwendung finden ist nicht sehr wahrscheinlich.</p>
</section>
<section>
<p>Wenn wir einen Trigger nicht mehr ausführen wollen, muss bei Variante 1 einfach die Property auf undefined gesetzt werden.</p>
<p>Für Variante 2 müssen wir die Funktion <code>removeEventListener</code> mit der Funktion aufrufen, mit der der Event-Listener initial zugewiesen wurde.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
function onBodyClick () {
console.log('Ich werde nur einmal aufgerufen!');
// Es muss die gleiche Funktion entfernt werden!
document.body.removeEventListener('click', onBodyClick);
}
// Wir sagen dem Element bei welchem Element eine Funktion ausgeführt wird (bei click, führe onBodyClick aus).
document.body.addEventListener('click', onBodyClick);
</code></pre>
</section>
<section>
<h3>Event-Informationen</h3>
</section>
<section>
<p>Wir haben nun Events erfolgreich mit Funktionen ausgestattet die bei ihrem entsprechenden Trigger ausgeführt werden.</p>
<p>Aber was wenn uns das nicht reicht?</p>
<p>Beispiel: Eine Klick-Funktion wird von mehreren Elementen aus aufgerufen... Woher wissen wir welches Element angeklickt wurde?</p>
</section>
<section>
<p>Wenn eine Funktion durch ein Event ausgeführt wird, erhält die Funktion ein Objekt als Parameter:</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
function onClick (params) {
console.log(params);
}
document.addEventListener('click', onClick);
</code></pre>
<p>Sehen wir uns die Event-Parameter genauer an</p>
</section>
<section>
<p>Event-Informationen</p>
<pre class="json"><code data-trim data-line-numbers is:raw>
{
"bubbles": true, // Event-Bubbling
"cancelable": true, // Event-Bubbling
"defaultPrevented": false, // Event-Bubbling
"currentTarget": null,
"eventPhase": 0, // Event-Bubbling
"isTrusted": true,
// "Tiefstes" Element das getroffen wurde
"target": HTMLElement,
"type": "click",
}
</code></pre>
</section>
<section>
<p>Event-Paremeter kann man sich wie Klassen vorstellen. Das <code>Event</code> ist eine Superklasse aus der neue Klassen gebildet werden.</p>
</section>
<section>
<p>Möglich Abstrahierungen (es gibt noch eine Menge mehr)</p>
<ul>
<li>ClipboardEvent</li>
<li>DragEvent</li>
<li>GamepadEvent</li>
</ul>
</section>
<section>
<p>In unserem Fall interagieren wir mit dem User-Interface. Dieses Event wird um Informationen aus einem sogenannten <code>UIEvent</code> erweitert:</p>
</section>
<section>
<p><code>UIEvent</code> Informationen</p>
<pre class="json"><code data-trim data-line-numbers is:raw>
{
// Bei Klick-Events die Anzahl der Klicks
"detail": 1,
"view": Window,
"which": 1, // Welcher Knopf
}
</code></pre>
</section>
<section>
<p><code>UIEvent</code> wird aber nochmals erweitert. Da wir auf einen Klick reagieren, haben wir nochmals Informationen über das sogenannte <code>MouseEvent</code></p>
<p>Weitere Mögliche Event-Typen sind</p>
<ul>
<li>TouchEvent</li>
<li>FocusEvent</li>
<li>KeyboardEvent</li>
<li>InputEvent</li>
</ul>
</section>
<section>
<p><code>MouseEvent</code> Informationen (Seite 1)</p>
<pre class="json"><code data-trim data-line-numbers is:raw>
{
"altKey": true,
"button": 0, // Welcher Knopf
"buttons": 0, // Welche Knöpfe
// Position im Viewport
"clientX": 286,
"clientY": 702,
"ctrlKey": false,
"metaKey": false,
// Für mousemove Events:
// Bewegungsrichtung seit letztem Event
"movementX": 0,
"movementY": 0,
}
</code></pre>
</section>
<section>
<p><code>MouseEvent</code> Informationen (Seite 2)</p>
<pre class="json"><code data-trim data-line-numbers is:raw>
{
"offsetX": 0,
"offsetY": 0,
// Position im gesamten Dokument
"pageX": 286,
"pageY": 702,
"relatedTarget": null,
// Position im gesamten Screen
"screenX": 286,
"screenY": 849,
"shiftKey": false,
// ClientX/Y Alias
"x": 286,
"y": 702,
}
</code></pre>
</section>
<section>
<p>Das war aber noch nicht alles. Das ganze wird nochmal Spezialisiert: <code>PointerEvent</code></p>
<p>Weitere Event-Typen</p>
<ul>
<li>WheelEvent</li>
<li>DragEvent</li>
</ul>
</section>
<section>
<p>Pointer-Event Informationen</p>
<pre class="json"><code data-trim data-line-numbers is:raw style="max-height: 500px;">
{
"altitudeAngle": 1.57,
"azimuthAngle": 0,
"height": 1,
"isPrimary": true,
"pointerId": 0,
"pointerType": "mouse",
"pressure": 0,
"rangeOffset": 0,
"rangeParent": null,
"returnValue": true,
"tangentialPressure": 0,
"tiltX": 0,
"tiltY": 0,
"twist": 0,
"width": 1,
}
</code></pre>
</section>
<section>
<h3>DOM-Events</h3>
</section>
<section>
<p>Es gibt 2 Events die das Dokument auslöst.</p>
<p>Sie signalisieren, dass das Dokument "vollständig" geladen wurde (und somit alle Statischen HTML-Elemente bereit sind) und wenn der User das aktuelle Fenster "verlässt" (Tab schließen, Fenster schließen)</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
document.addEventListener('DOMContentLoaded', () => {
console.log('Browser hat alle Daten geladen und geparsed!');
});
// Beachte: Das Event wird dem Fenster angehängt
window.addEventListener('beforeunload', (event) => {
// Bevor der Browser "aufräumt"...
event.preventDefault();
// Legacy-Support
event.returnValue = true;
})
</code></pre>
</section>
<section>
<h3>Event-Bubbling</h3>
</section>
<section>
<p>Was passiert wenn wir geschachtelte Elemente haben, auf denen jeweils click-listener lauschen?</p>
<p>Wenn wir auf ein Element im DOM klicken, arbeitet sich der Browser vom <code>html</code> Element aus, tiefer in das Dokument bis zum tiefsten Element das angeklickt wurde.</p>
<p>Wenn das tiefste Element erreicht wurde, löst der Browser das <code>click</code> Event in diesem Element aus.</p>
</section>
<section>
<p>Von dort aus ruft der Browser die <code>click</code> Funktionen der parent-Element nacheinander auf.</p>
<p>Wir können uns das so vorstellen, als würden wir in einem See herunter tauchen, dort austmen, und die Luftblase steigt wieder nach oben auf.</p>
<p>Entsprechend hat dieses System auch den Namen "Event-Bubbling" erhalten.</p>
</section>
<section>
<p>Wir können das Event-Bubbling aber stoppen.</p>
<p>Das <code>event</code> Objekt das wir in jeder Funktion erhalten hat eine Methode <code>stopPrpagation</code> mit der wir (weiteres) Event-Bubbling aufhalten können.</p>
</section>
<section>
<h3>Capture-Phase</h3>
</section>
<section>
<p>Wir haben darüber gesprochen, dass ein Click vom HTML-Element aus nach "unten" propagiert bevor das Event wieder durch alle Elemente "nach oben bubbled".</p>
<p>Dieses "nach-unten-propagieren" ist eine Event-Phase (diese haben wir vorher bereits im Event-Objekt gesehen), die wir allgemein als <strong>Capturing</strong> benennen können.</p>
<p>Und wir können auf diese Capture-Phase reagieren (anstatt in der Bubbling-Phase aufgerufen zu werden.)</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
element.addEventListener('click', () => {
console.log('Aufgerufen während der Bubbling-Phase');
});
element.addEventListener('click', () => {
console.log('Aufgerufen in der Capture-Phase');
},
// Achten Sie auf das zusätzliche true...
true);
</code></pre>
<button id="demo-button">
Clicke mich!
</button>
<script>
const b = document.getElementById('demo-button');
b?.addEventListener('click', () => console.log('Aufgerufen während der Bubbling-Phase'));
b?.addEventListener('click', () => console.log('Aufgerufen während der Capture-Phase'), true);
</script>
</section>
<section>
<h3>Custom Events</h3>
</section>
<section>
<p>Abseits von Events die durch HTML-Elemente ausgelöst werden, können wir auch "eigene" Events formulieren, darauf "hören" und auslösen.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
// Auf das neue Event hören
document.addEventListener('mein-event', () => {
console.log('"mein-event" wurde ausgelöst!');
});
// Der Name ist selbstgewählt
const myEvent = new CustomEvent('mein-event');
// Das Event "abfeuern"
document.dispatchEvent(myEvent);
</code></pre>
</section>
<section>
<p>Für Custom-Events gibt es noch viele weitere Features die man Entdecken kann. Lesen Sie sich die Dokumentation im Mozilla Developer Network durch!</p>
</section>
</section>

View file

@ -0,0 +1,89 @@
<section>
<section>
<h2>JavaScript &amp; Formulare</h2>
</section>
<section>
<p>Wir haben bereits viele DOM-FUnktionen kennen lernen dürfen.</p>
<p>Aber eines haben wir noch nicht genauer betrachtet:</p>
<p>Wie wir mit JavaScript und Formularen arbeiten können / müssen.</p>
</section>
<section>
<p>Manchmal reicht es nicht aus, das Standard-HTML-Formular direkt abzusenden.</p>
<p>Es kommen früher oder später Themen auf, die es erfodern per JavaScript in den Formular-Prozess einzugreifen.</p>
<p>Meistens sehen wir uns Daten an, weißen auf Fehlern in den Daten hin, oder versuchen auf fehlende Informationen hinzuweisen.</p>
<p>Zusätzlich müssen wir dafür Sorgen, dass keine Bots einfach Formulardaten absenden können.</p>
</section>
<section>
<h3>Auf Daten in Inputs zugreifen</h3>
</section>
<section>
<p>Wir können zu jederzeit die Daten aus einem Input auf 2 Arten auslesen:</p>
<p>1. Wir fragen die Daten aus dem HTML-Element ab</p>
<p>2. Wir erhalten ein "update" durch den <code>change</code> oder <code>keydown</code> Event-Listener</p>
</section>
<section>
<pre class="html"><code data-trim data-line-numbers>
&lt;input id="my-input" type="text" /&gt;
</code></pre>
<pre class="js"><code data-trim data-line-numbers is:raw>
const input = document.getElementById('my-input');
// Direkt auf die Daten zugreifen...
console.log(input.value);
// Mit einem keydown Event aufs target.value schauen
input.addEventListener('keydown', (event) => {
console.log(event.target.value);
})
</code></pre>
<input type="text" id="my-input" />
</section>
<section>
<p>Es gibt verschiedene Arten von Events für ein Input-Feld</p>
<ul>
<li>change (Browser-Abhängig wann)</li>
<li>keydown (Bei Tastendruck)</li>
<li>keyup (Beim Loslassen der Taste)</li>
</ul>
</section>
<section>
<p>Je nach <code>type</code>, finden wir den gesuchten Inhalt an anderer Stelle:</p>
<ul>
<li>Checkbox / Radio -&gt; .checked (boolean)</li>
<li>Text-basiert -&gt; .value (string)</li>
</ul>
<p>Select hat seinen aktuell ausgewählten Wert auch in <code>.value</code>. Der Wert ist aber das <code>value</code>-Attribut der Option (anstelle des Angezeigten Textes)</p>
</section>
<section>
<h3>Wert ändern</h3>
</section>
<section>
<p>Wir können den Wert eines jeden Inputs dahingehend ändern indem wir einfach das <code>.value</code> (beziehungsweise das <code>.checked</code>-Attribut bei Checkboxen oder Radios) überschreiben.</p>
</section>
<section>
<h3>Eingabedaten im Rahmen eines Formulars</h3>
</section>
<section>
<p>Daten die Innerhalb eines html <code>form</code> Elementes eingegeben worden sind, können während des <code>submit</code> Events "abgefangen" werden.</p>
<p>Wenn keine Action für das Formular definiert ist, wird die Action-URL als aktuelle URL gesetzt (und somit bei einem submit auch Daten an diese URL versendet, was wiederum zu einem Reload führt)</p>
<p>Diese Standard-Aktion des Forms (sowie viele andere Standard-Aktionen anderer Elemente) kann abgebrochen werden, indem auf dem event-Objekt <code>.preventDefault()</code> aufgerufen wird.</p>
</section>
<section>
<p>Wir können zu jederzeit auf die Inhalte eines Formulars zugreifen indem wir eine spezielle Klasse nutzen: <code>FormData</code>.</p>
<p>Wir können dieser Klasse beim erstellen das Formular-Element mitgeben. Der Browser wird dann alle Eingabefelder in resultierenden Objekt anhand des <code>name</code> Attributs als key hinterlegen.</p>
<p>Achtung: Es werden nur Daten in das FormData Objekt aufgenommen, dessen <code>input</code> nicht <code>disabled</code> ist.</p>
</section>
</section>

View file

@ -0,0 +1,55 @@
<section>
<section>
<h2>Elemente aus dem DOM Abfragen</h2>
</section>
<section>
<p>Wir kennen nun alle wichtigen JavaScript Features um die erste wichtige API innerhalb des Browsers kennen zu lernen: Die DOM-API.</p>
<p>Mithilfe dieser können wir mit dem HTML-Dokuments selbst interagieren: Elemente suchen, modifizieren, neue Elemente hinzufügen und Elemente auch wieder löschen.</p>
<p>Alle Relevanten Funktionen sind in dem globalen Objekt <code>document</code> beinhaltet.</p>
</section>
<section>
<strong><code>document.getElementById(id)</code></strong>
<p>Sucht im Dokument nach einem HTML-Element mit der Angegebenen <strong>ID</strong>.</p>
<p>Wenn kein Element gefunden wird, liefert die Funktion <strong><code>null</code></strong>.</p>
<p>Gibt es mehrere Elemente mit der gleichen ID, wird das erste gefundene geliefert.</p>
</section>
<section>
<strong><code>document.querySelector(selector)</code></strong>
<p>Sucht im Dokument nach einem HTML-Element mit der Angegebenen <strong>CSS-Query</strong>.</p>
<p>Wenn kein Element gefunden wird, liefert die Funktion <strong><code>null</code></strong>.</p>
<p>Gibt es mehrere Dokumente die dem Selektor matchen, wird das zuerst gefundene Element zurück geliefert.</p>
</section>
<section>
<strong><code>document.querySelectorAll(selector)</code></strong>
<p>Ähnlich zu <code>querySelector</code>, liefert aber alle gefundenen Elemente in einem Array-Ähnlichen Konstrukt zurück (Static NodeList).</p>
<p>Gibt es keine Elemente, ist dieses Array leer.</p>
<p>Die Elemente im Array werden <strong>nicht</strong> dynamisch aktualisiert.</p>
<p>Mit <code>Array.from(NodeList)</code> kann die NodeList in ein normales Array überführt werden.</p>
</section>
<section>
<h3>"Tree-Walking"</h3>
</section>
<section>
<p>Das Objekt <code>document</code> beinhaltet auch direkten Zugriff auf das HTML-Dokument.</p>
<p>In <code>document.body</code> ist die Referenz zum <code>body</code>-Tag des HTML-Dokumentes zu finden.</p>
<p>Von hier aus (oder von jedem anderen HTML-Element) kann man nun den Document-Tree Auf-, Ab- und Be-Wandern.</p>
</section>
<section>
<code>element.nextSibling & element.previousSibling</code>
<p>Zugriff auf das nächste / vorherige Element in der gleichen Ebene (gleiches Parent)</p>
<hr>
<code>element.children[]</code>
<p>Zugriff auf die Kind-Elemente eines Elementes</p>
<hr>
<code>element.parentNode & element.parentElement</code>
<p>Zugriff auf das Parent-Element</p>
</section>
</section>

View file

@ -0,0 +1,19 @@
---
import Title from "./title.astro";
import GettingElements from "./getting-elements.astro";
import ModifyElements from "./modify-elements.astro";
import InsertElements from "./insert-elements.astro";
import DeleteElements from "./delete-elements.astro";
import Forms from "./forms.astro";
import Events from "./events.astro";
---
<div class="slides">
<Title />
<GettingElements />
<ModifyElements />
<InsertElements />
<DeleteElements />
<Events />
<Forms />
</div>

View file

@ -0,0 +1,24 @@
<section>
<section>
<h2>Neue Elemente hinzufügen</h2>
</section>
<section>
<p>Neben <code>innerHTML</code> gibt es noch eine weitere Möglichkeit Elemente zu erstellen.</p>
<p>Wir nutzen dafür die Funktion <code>document.createElement(tag)</code> um ein Element zu erstellen.</p>
<p>Das Element können wir noch "konfigurieren" (Text, Attribute, Events) und können dieses dann einem beliebigen Element anfügen (mit der Element-Funktion <code>appendChild</code>)</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
// Button erstellen
const newButton = document.createElement('button');
// wir können noch mehr mit dem button machen (klick-events, attribute, etc);
newButton.innerText = "Klick mich!";
// Wir können einem beliebigen Element nun den Button als Kind anhängen.
document.body.appendChild(newButton);
</code></pre>
</section>
</section>

View file

@ -0,0 +1,97 @@
<section>
<section>
<h2>Elemente mit JavaScript modifizieren</h2>
</section>
<section>
<p>Wir haben nun unser Element gefunden, was können wir nun alles damit tun?</p>
<p>Eigentlich <strong>alles</strong>. Wir können den Inhalt des Elementes ändern, Attribute ändern, Klassennamen hinzufügen / löschen und vieles mehr.</p>
</section>
<section>
<h3>Attribute ändern</h3>
</section>
<section>
<p>
<code>$0.getAttribute(attributeName)</code><br>
Um ein Attribut abzufragen, null wenn nicht gesetzt.
</p>
<hr>
<p>
<code>$0.setAttribute(name, value)</code><br>
Setzt auf dem Element ein Attribute mit gegebenen <strong>Namen</strong> und <strong>Value</strong>.
</p>
<hr>
<p>
<code>$0.removeAttribute(name)</code><br>
Entfernt ein Attribut von einem HTML-Element.
</p>
</section>
<section>
<h3>Klassen</h3>
</section>
<section>
<p>Da Klassen auf einem Element häufiger geändert werden, gibt es hierfür ein spezialisiertes Objekt am HTML-Element um die Klassen zu modifizieren.</p>
<p>Dieses befindet sich unter dem Namen <strong>element.classList</strong> innerhalb des Elementes.</p>
</section>
<section>
<table>
<tr>
<th>Funktion</th>
<th>Beschreibung</th>
</tr>
<tr>
<td>.add(className)</td>
<td>Fügt den gegebenen Klassennamen dem Element hinzu</td>
</tr>
<tr>
<td>.contains(className)</td>
<td><code>true</code>, wenn Klassenname in Liste beinhaltet ist, ansonsten <code>false</code></td>
</tr>
</table>
</section>
<section>
<table>
<tr>
<th>Funktion</th>
<th>Beschreibung</th>
</tr>
<tr>
<td>.remove(className)</td>
<td>Entfernt den gegebenen Klassennamen</td>
</tr>
<tr>
<td>.replace(oldClass, newClass)</td>
<td>Tauscht <code>oldClass</code> durch <code>newClass</code> aus</td>
</tr>
<tr>
<td>.toggle(className)</td>
<td>Entfernt Klassennamen wenn existent, ansonsten wird der Klassenname hinzugefügt.</td>
</tr>
</table>
</section>
<section>
<h3>innerText</h3>
</section>
<section>
<p><code>element.innerText</code> ist ein String, der den aktuellen Text des Elementes beinhaltet.</p>
<p>Der Text ist so strukturiert, als würde man den Text auf der Website selektieren und kopieren (enthält also keine HTML-Kind-Elemente, sondern nur dessen Text)</p>
<p>Der Text kann geändert indem die Variable / die Property mit einem neuen Text "assigned" wird. Newlines werden dabei durch <code>&lt;br&gt;</code> ausgetauscht.</p>
</section>
<section>
<h3>innerHTML</h3>
</section>
<section>
<p>Im Kontrast zu <code>innerText</code>, beinhaltet <code>innerHTML</code> auch alle HTML-Tags die sich innerhalb des ausgewählten Elementes befinden.</p>
<p>Auch hier können wir den Wert überschreiben. Befindet sich eine HTML-Syntax im neuen String, wird dies auch als HTML interpretiert.</p>
</section>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>JavaScript<br>Mit dem Dokument Interagieren</h1>
</section>

View file

@ -0,0 +1,8 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/javascript/05-dom/index.astro"
---
<Reveal title="JavaScript - Interacting with the DOM">
<Slides />
</Reveal>