Web-Entwicklung

Agenda

Grundlagen

  • HTML/CSS/JS
  • HTTP-Protokoll
  • JSON

Fortgeschrittenes

  • JavaScript
  • Responsive Design
  • NodeJS
  • WebComponents / lit
  • Datenbank

Architektur & Paradigmen

  • Funktionale Programmierung
  • Objektorientierte Programmierung
  • Entwurfsmuster (Singleton, Factory, Builder...)
  • SOLID-Prinzipien
  • Dependency Injection
  • MVC
  • Redux

Interessante Technologien

  • Canvas-Element
  • CSS-Animationen
  • WebSockets
  • WebWorker
  • ServiceWorker
  • WebRTC
  • WebGL
  • Progressive Web Application
  • CEF / Electron
  • WebAssembly

Betrieb

  • Server einrichten
  • SSL- / TLS-Zertifikate

Workflows

  • Git
  • Test Driven Development
  • Continuous Integration / Continuous Delivery

HTML

  • HyperText Markup Language
  • Erste Version 1993 von Tim Berners-Lee
  • Dokumentationsmedium
  • Ursprünglich rein akademische Verwendung
  • Verlinkungen der Dokumente macht sie "hyper"
VersionErscheinungElemente ca.
HTML 1.0199320
HTML 2.0199550
HTML 3.2199770
HTML 4.01199990
HTML 5.02014110
  • Heute ein "lebender Standard" (Living Standard)
  • Stetige inkrementelle Erweiterung

Grundgerüst

							
								
							
						

Beispiel

							
								
							
						

Beispiel

Aufbau eines Elements

							
								
							
						
							
								
							
						
							
								
							
						

CSS

  • Cascading Style Sheet
  • Nach größerer Verbreitung von HTML
  • Webseiten ansprechend gestalten

Beispiel

							
								selector [, selector, selector, ...] {
									property: value;
								}
							
						

Beispiel

							
								html {
									background-color: red;
									font-size: 30px;
								}
							
						

Beispiel

							
								
							
						

Beispielhafte CSS-Eigenschaften

  • background-color: red
  • font-family: Georgia, serif, Arial
  • text-decoration: line-through underline

Selektoren

  • Umfangreiche Element-Selektoren zur Anwendung der Styles
  • Kinder, Enkel...
  • Geschwister, direkte Nachfolger
  • Attribute
  • Pseudoklassen

Selektoren

							
								#my-id /* ID */
								html, body /* Mehrfachselektion */
								div > p /* p die direkt unterhalb eines div sind */
								div  p /* p die irgendwo unterhalb eines div sind */
								a:visited /* besuchte Links */
								div:hover /* divs über die man mit der Maus fährt */
								span.important /* span mit der Klasse "important" */
							
						

Layout

Es gibt zwei grundlegende Blocktypen:

block

inline

Block

Beansprucht eine ganze Zeile und verursacht Zeilenumbrüche

							
								display: block;
							
						

Kann Breite und Höhe haben

							
								display: block;
								width: 20px;
								height: 20px;
							
						

Inline

Wie einfacher Text, der mit anderen in der gleichen Zeile stehen kann

							
								display: inline;
							
						

Festlegen von Breite und Höhe sind wirkungslos

							
								display: inline;
								width: 20px; /* kein Effekt */
								height: 20px; /* kein Effekt */
							
						

Inline-Block

Mischform von inline und block

							
								display: inline-block;
								width: 20px;
								height: 20px;
							
						

Spezifität

Regeln können andere Regeln überschreiben

							
								
							
						
							
								#id:hover { display: none }

								#id { display: block }

								.class.clazz { display: inline }

								div { display: inline-block }
							
						

Box Model

Es gibt zwei Varianten des Modells:

content-box (default)

border-box

							
								box-sizing: content-box;
								box-sizing: border-box;
							
						

Beispiel

							
								div {
									border: 3px solid red;
									padding: 10px;
									width: 100px;
								}

								.border-box {
									box-sizing: border-box;
								}

								.content-box {
									box-sizing: content-box;
								}
							
						
							
								
							
						

Beispiel

Positioning

							
								/* default, da wo es in der Seite steht */
								position: static;
								/* relativ zu seiner eigentlichen Position */
								position: relative;
								/* relativ zu seinem nächsten non-static parent o. window */
								position: absolute;
							
						
							
								position: relative;
								position: absolute;
							
						

erlauben Verschiebung mittels

							
								top: 10px;
								left: 10px;
								right: 10px;
								bottom: 10px;
							
						

Beispiel Menü

							
								.menu-item {
									display: inline-block;

									position: relative;
								}

								.menu-item .children {
									display: none;

									position: absolute;
								}

								.menu-item:hover .children {
									display: block;
								}
							
						
							
								
							
						

JS

  • JavaScript stammt aus dem Jahr 1995 von Netscape
  • Syntaktisch an Java angelehnt
  • Ursprünglich nur optionale, verzichtbare Effekte (unobstrusive)
  • Mittlerweile von ECMA standardisiert
VersionECMA StandardJahr
ES1ECMAScript 11997
ES2ECMAScript 21998
ES3ECMAScript 31999
ES5ECMAScript 52009
ES6ECMAScript 20152015
...
ECMAScript 20202020

Code wird zwischen script-Tags geschrieben

							
								<!DOCTYPE html>
								<html lang="de">
									<head>
										<meta charset="UTF-8">
									</head>
									<body>
										<script>// code</script>
									</body>
								</html>
							
						

DOM-API

							
								// findet das erste Element
								const parent = document.querySelector("#id");

								// findet alle Elemente
								const elements = document.querySelectorAll("#id");

								// Element erstellen
								const newElement = document.createElement("div");

								newElement.addEventListener("click", (event) => {
									// Click-Event
								});

								// Element anhängen
								parent.appendChild(newElement);

								newElement.remove();
							
						

Auslagern in verschiedene Dateien

							
								<!DOCTYPE html>
								<html>
									<head>
										
										
									</head>
									<body>
										
										<script src="index.js"></script>
									</body>
								</html>
							
						

Parsen eines HTML-Dokuments

							
								
							
						

Verschiedene Varianten, JavaScript erst am Ende auszuführen

Damals mit jQuery:

							
								<!DOCTYPE html>
								<html>
									<head>
										<script>
											$(document).ready((event) => {
												const element = document.querySelector("#hello");
											});
										</script>
									</head>
									<body>
										
Hello World!
</body> </html>

script-Tag am Ende

							
								<!DOCTYPE html>
								<html>
									<head>
									</head>
									<body>
										
Hello World!
... <script src="index.js"></script> </body> </html>

Event-basiert, z.B. per DOMContentLoaded

							
								<!DOCTYPE html>
								<html>
									<head>
										<script>
											document.addEventListener("DOMContentLoaded", (event) => {
												// wird ausgeführt, sobald das Dokument geparst wurde
												const element = document.querySelector("#hello");
											});
										</script>
									</head>
									<body>
										
Hello World!
</body> </html>

Moderne Variante

							
								<script src="index.js" defer></script>
							
						

document-Operationen

							
								document.open();
								document.write("
Booo!
"); document.close();
							
								<body>
									<script>document.write("

Dies ist ein Text.

")</script> <script> const button = document.querySelector("button"); button.addEventListener("click", () => { document.write("Und pfutsch!"); }); </script> </body>

HTTP-Protokoll

  • OSI-Schichten 5, 6 und 7
  • Anfrage-Antwort-Prinzip
  • Basiert auf einfachem Text
VersionJahr
0.91991
1.01996
1.11999
2.02015
3.02022

Request an www.google.de

							
								GET / HTTP/1.1
								Host: www.google.de
								User-Agent: curl/7.88.1
								Accept: */*
							
						

Response von www.google.de

							
								HTTP/1.1 200 OK
								Date: Tue, 04 Apr 2023 15:45:26 GMT
								Expires: -1
								Cache-Control: private, max-age=0
								Content-Type: text/html; charset=ISO-8859-1
								Content-Security-Policy-Report-Only: [...]
								Server: gws
								X-XSS-Protection: 0
								X-Frame-Options: SAMEORIGIN
								Set-Cookie: [...]
								Accept-Ranges: none
								Vary: Accept-Encoding
								Transfer-Encoding: chunked
								
								<!doctype html><html>...</html>
							
						

Einfach mal testen:

							
								curl -v --http1.0 http://www.google.de
								curl -v --http1.1 http://www.google.de
								curl -v --http2 http://www.google.de
								curl -v --http2-prior-knowledge http://www.google.de
								curl -v --http3 http://www.google.de
							
						

Verschiedene "Verben" bzw. "Methoden"

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
  • ...

Beispiel:

							
								git clone https://git.furnco.de/r/public/web-development/examples.git
								cd examples/templates/005-http-rest
								node index.js
							
						
							
								curl -X GET -is http://localhost:3000/todo
								curl -X POST -H "Content-Type: application/json" -d '{"name": "Einkaufen"}' -is http://localhost:3000/todo
								curl -X POST -H "Content-Type: application/json" -d '{"name": "Putzen"}' -is http://localhost:3000/todo
								curl -X PUT -H "Content-Type: application/json" -d '{"name": "Schlafen"}' -is http://localhost:3000/todo/1
								curl -X DELETE -is http://localhost:3000/todo/0
							
						
							
								HTTP/1.1 200 OK
								X-Powered-By: Express
								Content-Type: application/json; charset=utf-8
								Content-Length: 13
								ETag: W/"d-KMmUcQ1kigqtwzZnU+jVDkYP3Co"
								Date: Wed, 05 Apr 2023 12:05:05 GMT
								Connection: keep-alive
								Keep-Alive: timeout=5
								
								["Einkaufen"]
							
						
							
								app.get("/todo", (req, res) => {
									return res.json(todos);
								});
								
								// [...]
								
								app.post("/todo", (req, res) => {
									// [...]
										return res.json(todos);
									// [...]
								});
							
						

GET und POST auf klassischen Webseiten

							
								

Beispiel:

							
								cd examples/templates/006-http-web
								node index.js
							
						

JSON

  • JavaScript Object Notation
  • Basiert auf reinem Text
  • Format zur strukturierten Speicherung von Daten
  • Attribute müssen immer in doppelte Anführungsstriche (") gesetzt werden

Definition eines einfachen Objekts in JS

							
								const o = {
									x: 1,
									y: 2
								};
							
						

Entsprechung in JSON

							
								{
									"x": 1,
									"y": 2
								}
							
						

Mögliche Typen

  • Number (bzw. Integer / Float)
  • Boolean
  • String
  • Array
  • Object

Der äußere Container kann ein Array oder ein Object sein

							
								{
									"key": "value",
								}
							
						
							
								[
									"foo",
									"bar"
								]
							
						

JavaScript

c / c++ Java C# JS
Formale Spezifikation ja
Übersetzung Nativ kompiliert Bytecode Interpretiert / kompiliert (JIT)
Typisierung statisch dynamisch
Speicher-Management Manuell Garbage Collector

Begrifflichkeiten

							
								function add(x, y) {
									return x + y;
								}
								
								const a = 5;
								const b = add(a, 6);
								const c = [];
							
						
Operator, Aufruf (Call), Ausdruck (Expression), Parameter, Argument, Literal,
Zuweisung (Assignment), Linke Hand (Left hand), Rechte Hand (Right hand)

Vorbereiten der Beispiele (bei Bedarf)

							
								# im Ordner 'examples/'
								. hooks/post-checkout
							
						

Ausführung

							
								# node [Script-Datei], z.B.
								node index.js
							
						

Ausgaben

							
								console.log("Hello World!");
							
						

Variablen

							
								//  < es6
								var x = 1;
								
								// >= es6
								let y = 2;
								const z = 3;
							
						
							
								var yes = false;

								if (yes) {
									var a = 1;
								}
								
								console.log(a);
							
						
							
								undefined
							
						
							
								var yes = false;

								if (yes) {
									let a = 1;
								}
								
								console.log(a);
							
						
							
								ReferenceError: a is not defined
							
						
							
								function foo() {
									var yes = false;
								
									if (yes) {
										var a = 1;
									}
								}
								
								foo();
								
								console.log(a);
							
						
							
								ReferenceError: a is not defined
							
						

var

  • hat function-Scope
  • wird durch hoisting ("hochziehen") aus dem Block heraus an den Start der Funktion gezogen
							
								var yes = false;
								var a;

								if (yes) {
									a = 1;
								}
								
								console.log(a);
							
						

let, const

  • haben intuitiven Block-Scope (wie viele andere Programmiersprachen auch)
  • daher nur an Ort und Stelle gültig in der angegebenen Reihenfolge

Datentypen

							
								let x = 5; // Number (Integer)
								let f = 1.25; // Number (Float)
								let b = true; // Boolean
								let s = "hallo"; // String
								let a = []; // Array
								let o = {}; // Objekt
								let nan = NaN; // Not a Number
								let n = null; // Null
								let u = undefined; // Undefiniert
							
						

Alternation

							
								if (x == 1) {
									// Code
								}
								else if (x == 2) {
									// alternativer Code
								}
								else {
									// Fallback
								}
							
						

Schleifen

							
								for (let i = 0; i < 5; i++) {
									// Code
								}
							
						
							
								const a = [1, 2, 3];
								
								for (const element of a) {
									// Code
								}
							
						
							
								while (i == 1) {
									// Code
								}
							
						
							
								do {
									// Code
								} while (i == 1)
							
						

Arrays

							
								let a = [1, 2, 3];
								
								a.push(4);
								a.push(5);
								
								a.pop();
								
								console.log(a);
							
						
							
								[1, 2, 3, 4]
							
						

Objekte

							
								let o = {x: 1, y: 2};
								
								console.log(o.z);
								
								console.log(o);
								
								o.z = 3;
								
								console.log(o);
							
						
							
								undefined
								{ x: 1, y: 2 }
								{ x: 1, y: 2, z: 3 }
							
						
Objekte funktionieren intern wie Hash-Maps
							
								let o = {};
								
								o.a = 1;
								o["a"] = 1; // alternativ
								
								o["$ !"] = 2; // so sind auch komplexe Schlüssel möglich
								
								// o."$ !" = 2; => Syntaxfehler
								// o.$ ! = 2; => Syntaxfehler
								
								o[true] = 3;
								
								console.log(Object.keys(o));
							
						
							
								[ 'a', '$ !', 'true' ]
							
						

Funktionen

							
								function foo() {
									console.log("foo");
								}
								
								function bar(a) {
									return a;
								}
								
								foo();
								
								const a = bar(1);
								
								console.log(a);
							
						
							
								foo
								1
							
						
							
								let sum = function(a, b) {
									return a + b;
								}
								
								let subtract = function() {
									console.log(arguments);
								
									return arguments[0] - arguments[1];
								}
								
								console.log(sum(1, 2));
								console.log(subtract(1, 2));
							
						
							
								3
								[Arguments] { '0': 1, '1': 2 }
								-1
							
						

Funktionen als Konstruktor

							
								function Rectangle(a, b) {
									this.a = a;
									this.b = b;
								}
								
								const r = new Rectangle(2, 3);
								
								console.log(r.a);
							
						
							
								2
							
						

Objektorientierung

							
								function Rectangle(a, b) {
									this.a = a;
									this.b = b;
								}
								
								// jede Funktion besitzt einen Prototypen
								Rectangle.prototype.getArea = function() {
									return this.a * this.b;
								}
								
								const r1 = new Rectangle(2, 3);
								const r2 = new Rectangle(4, 5);
								
								console.log(r1.getArea());
								console.log(r2.getArea());
							
						
							
								6
								20
							
						
Dynamische Änderung des Prototypen
							
								function Rectangle(a, b) {
									this.a = a;
									this.b = b;
								}
								
								const r = new Rectangle(2, 3);
								
								console.log(r.getArea);
								
								Rectangle.prototype.getArea = function() {
									return this.a * this.b;
								}
								
								console.log(r.getArea);
							
						
							
								undefined
								[Function (anonymous)]
							
						

Aufgabe

Bringe dem Array eine Methode bei, welche die Summe aller enthaltenen Zahlen zurückgibt
							
								const a = [1, 2, 3];
								
								console.log(a.sum());
							
						
							
								6
							
						

Lösung

							
								Array.prototype.sum = function() {
									let sum = 0;
								
									for (let number of this) {
										sum += number;
									}
								
									return sum;
								}
							
						

Vererbung

							
								// Oberklasse
								function Shape() {
								}
								
								Shape.prototype.getType = function() {
									return "Shape";
								}
								
								// Unterklasse
								function Rectangle(a, b) {
									this.a = a;
									this.b = b;
								}
								
								// Prototype der Oberklasse in die Unterklasse kopieren
								Rectangle.prototype = Object.create(Shape.prototype);
								// Konstruktor der Oberklasse am Prototypen durch den eigenen ersetzen
								Rectangle.prototype.constructor = Rectangle;
								
								const r = new Rectangle(2, 3);
								
								console.log(r.getType());
								
								// .getType() selber implementieren
								Rectangle.prototype.getType = function() {
									return "Rectangle";
								}
								
								console.log(r.getType());
							
						
							
								Shape
								Rectangle
							
						

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

Objektorientierung in es6

							
								class Rectangle {
									constructor(a, b) {
										this.a = a;
										this.b = b;
									}
								
									getArea() {
										return this.a * this.b;
									}
								}
								
								const r = new Rectangle(2, 3);
								
								console.log(r.getArea());
							
						
							
								6
							
						

Vererbung in es6

							
								// Oberklasse
								class Shape {
									getType() {
										return "Shape";
									}
								}
								
								// Unterklasse erbt von Oberklasse
								class Rectangle extends Shape {
									constructor(a, b) {
										super();
								
										this.a = a;
										this.b = b;
									}
								}
								
								const r = new Rectangle(2, 3);
								
								console.log(r.getType());
								
								// Implementierung per Prototype weiterhin möglich, falls man so will
								Rectangle.prototype.getType = function() {
									return "Rectangle";
								}
								
								console.log(r.getType());
							
						
							
								Shape
								Rectangle
							
						

Asynchronität

Asynchron: Es wird nicht auf die Beendigung des Befehls gewartet, sondern mit dem Programm fortgefahren. Die Vollendung des asynchronen Befehls tritt irgendwann in der Zukunft ein.

							
								setTimeout(callback, time); // Ausführung nach [time] Millisekunden
								setInterval(callback, interval); // Ausführung alle [interval] Millisekunden
							
						
							
								setTimeout(function() {
									// Code, der nach 2 Sekunden ausgeführt wird
									console.log("zweiter");
								}, 2000);
								
								console.log("erster");
							
						
							
								erster
								zweiter
							
						

Aufgabe: Schreibe ein Programm, das zwei weitere beliebige Texte mit jeweils 2 Sekunden Verzögerung von einander ausgibt, d.h.

"erster" -> 2s -> "zweiter" -> 2s -> "Text A" -> 2s -> "Text B"

							
								setTimeout(function() {
									console.log("zweiter");
								
									// weiterer Code
								}, 2000);
								
								console.log("erster");
							
						
Lösung: Callback-Hell oder Pyramide des Todes
							
								setTimeout(function() {
									console.log("zweiter");
								
									setTimeout(function() {
										console.log("Text A");
								
										setTimeout(function() {
											console.log("Text B");
										
											
										}, 2000);
									}, 2000);
								}, 2000);
								
								console.log("erster");
							
						
Hilfsmittel: Promises
							
								new Promise(function (resolve, reject) {
									// ich bin fertig
									resolve();
								
									// es ist ein Fehler aufgetreten
									reject();
								});
							
						
							
								const p = new Promise(function (resolve, reject) {...});
								const p2 = new Promise(function (resolve, reject) {...});
								const p3 = new Promise(function (resolve, reject) {...});
								const p4 = new Promise(function (resolve, reject) {...});
								
								p
									.then(function() { return p2; })
									.then(function() { return p3; })
									.then(function() { return p4; });
							
						
							
								console.log("erster");
								
								delay(2000)
									.then(function () {
										console.log("zweiter");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text A");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text B");
									})
							
						
Aufgabe: Implementiere die Funktion "delay"
							
								const delay = function() {
									// implementieren
								}
								
								console.log("erster");
								
								delay(2000)
									.then(function () {
										console.log("zweiter");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text A");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text B");
									})
							
						
Lösung:
							
								const delay = function(ms) {
									return new Promise(function(resolve, reject) {
										setTimeout(function() {
											resolve();
										}, ms);
									});
								}
								
								console.log("erster");
								
								delay(2000)
									.then(function () {
										console.log("zweiter");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text A");
								
										return delay(2000);
									})
									.then(function() {
										console.log("Text B");
									})
							
						
Rückgabewerte mit Promises
							
								const p = new Promise(function(resolve, reject) {
									resolve("ok");
								});
								
								p.then(function(value) {
									console.log(value);
								});
							
						
							
								ok
							
						
Fehlerbehandlung mit Promises
							
								const p = new Promise(function(resolve, reject) {
									reject(new Error("Fehler"));
								});
								
								p
									.then(function() {
										console.log("Ich werde nicht aufgerufen")
									})
									.catch(function(e) {
										console.log(e.message);
									});
							
						
							
								Fehler
							
						
Datei lesen mit NodeJS
							
								const fs = require("fs");
								
								fs.readFile("./hello.txt", {encoding: "utf8"}, function(error, data) {
									console.log(data);
								});
							
						
							
								Hello World!
							
						
Aufgabe: "Promisifiziere" analog zu "delay" in der letzten Aufgabe den Aufruf von fs.readFile()
							
								const fs = require("fs");
								
								function readFile() {
									// implementieren
								}
								
								readFile("./hello.txt").then(function(content) {
									console.log(content);
								});
							
						
							
								Hello World!
							
						
Lösung:
							
								const fs = require("fs");
								
								function readFile(path) {
									return new Promise(function (resolve, reject) {
										fs.readFile(path, {encoding: "utf8"}, function(error, data) {
											resolve(data);
										});
									});
								}
								
								readFile("./hello.txt").then(function(content) {
									console.log(content);
								});
							
						
							
								Hello World!
							
						
Aufgabe: Nutze die vorherige Lösung nun für eine Promise-Kette, sodass die Inhalte der zwei Dateien "hello.txt" und "hallo.txt" nacheinander ausgegeben werden:
							
								Hello World!
								
								Hallo Welt!
								
							
						
Lösung:
							
								const fs = require("fs");
								
								function readFile(path) {
									return new Promise(function (resolve, reject) {
										fs.readFile(path, {encoding: "utf8"}, function(error, data) {
											resolve(data);
										});
									});
								}
								
								readFile("./hello.txt")
									.then(function(content) {
										console.log(content);
								
										return readFile("./hallo.txt");
									})
									.then(function(content) {
										console.log(content);
									});
							
						
							
								Hello World!
								
								Hallo Welt!
							
						

Async - Await

  • Syntaktischer Zucker für Promises
  • Nutzung von await nur im Kontext einer async-Funktion möglich
							
								async function wrapper() {
									const p = new Promise(function(resolve, reject) {
										resolve("Hello from Promise!");
									});
									
									const result = await p;
									
									console.log(result);
								}
								
								wrapper();
							
						
							
								Hello from Promise!
							
						
Aufgabe: Schreibe die Lösung der vorherigen Aufgabe nach Async - Await um
Lösung:
							
								const fs = require("fs");
								
								function readFile(path) {
									return new Promise(function (resolve, reject) {
										fs.readFile(path, {encoding: "utf8"}, function(error, data) {
											resolve(data);
										});
									});
								}
								
								async function wrapper() {
									const content1 = await readFile("./hello.txt");
									const content2 = await readFile("./hallo.txt");
								
									console.log(content1);
									console.log(content2);
								}
								
								wrapper();
							
						
							
								Hello World!
								
								Hallo Welt!
							
						

Destructuring

Auflösen von komplexen Strukturen

							
								// Objekt destrukturieren (per Attribut-Namen)
								const { a, b } = { a: 1, b: 2 };
								
								console.log(a);
								console.log(b);
								
								// Array destrukturieren (per Position)
								const [ c, d ] = [3, 4];
								
								console.log(c);
								console.log(d);
							
						
							
								1
								2
								3
								4
							
						
							
								// Bei Objekten Elemente umbenennen oder auch auslassen
								const { a: x, b: y } = { a: 1, b: 2, c: 3 };
								
								console.log(x);
								console.log(y);
								
								// Bei Arrays Elemente auslassen
								const [ , a, b, , c] = [3, 4, 5, 6, 7, 8];
								
								console.log(a);
								console.log(b);
								console.log(c);
							
						
							
								1
								2
								4
								5
								7
							
						
							
								// Verschachteltes Destructuring
								const {a: [, x, ] } = { a: [0, 1, 2]};
								
								console.log(x);
							
						
							
								1
							
						
Aufgabe: x soll die 5 enthalten
							
								const o = { a: [0, {b: [0, 5]}, 2]};
								
								const  { /* Destructuring-Ausdruck */ } = o;
								
								console.log(x);
							
						
							
								5
							
						
Lösung:
							
								const o = { a: [0, { b: [0, 5] }, 2]};

								const { a: [, { b: [, x] }]} = o;
								
								console.log(x);
							
						
							
								5
							
						
Aufgabe: x soll die 3 enthalten
							
								const o = { a: [0, {b: [0, { c: [{ x: 5 }, { d: 3}] }]}, 2]};
								
								const  { /* Destructuring-Ausdruck */ } = o;
								
								console.log(x);
							
						
							
								3
							
						
Lösung:
							
								const o = { a: [0, {b: [0, { c: [{ x: 5 }, { d: 3}] }]}, 2]};

								const  { a: [, {b: [, { c: [, { d: x}] }] }] } = o;
								
								console.log(x);
							
						
							
								3
							
						

Destructuring zum parallelen Ausführen von mehreren Promises

							
								const fs = require("fs");
								
								function readFile(path) {
									return new Promise(function (resolve, reject) {
										fs.readFile(path, {encoding: "utf8"}, function(error, data) {
											resolve(data);
										});
									});
								}
								
								async function wrapper() {
									const [ content1, content2 ] = await Promise.all([
										readFile("./hello.txt"),
										readFile("./hallo.txt")
									]);
								
									console.log(content1);
									console.log(content2);
								}
								
								wrapper();
							
						

Try-Catch-Finally

							
								function throwError() {
									try {
										throw new Error("Fehler!");
										return "Hello World!";
									}
									catch (e) {
										return e.message;
									}
									finally {
										console.log("Finally wird immer ausgeführt");
										return "Finally!";
									}
								}
								
								const result = throwError();
								console.log(result);
							
						
							
								Finally wird immer ausgeführt
								Finally!
							
						

Anwendungsbeispiel für Try-Catch-Finally

							
								function readFromDatabase() {
									try {
										db.connect();
										return db.read();
									}
									catch (e) {
										console.log(e.stack);
										return "";
									}
									finally {
										if (db.open) {
											db.close();
										}
									}
								}
							
						

Scope

							
								function a() {
									const x = 5;
								
									return function() {
										return x * x;
									};
								}
								
								const squareX = a();
								const result = squareX();
								
								console.log(result);
							
						
							
								25
							
						

this

							
								class MyClass {
									constructor(a) {
										this.a = a;
									}
								
									getA() {
										return this.a;
									}
								}
								
								const x = new MyClass(5);
								const y = new MyClass(10);
								
								console.log(x.getA());
								console.log(y.getA());
							
						
							
								5
								10
							
						
							
								global.a = 5;

								function foo() {
									this.a += 10;
								}
								
								foo();
								
								console.log(a);
							
						
							
								15
							
						
							
								class MyClass {
									/* ... */
									setA() {
										function f() {
											this.a += 100;
										}
										f();
									}
								}
								
								const x = new MyClass(5);
								
								x.setA();
								
								console.log(x.getA());
							
						
							
								TypeError: Cannot read property 'a' of undefined
							
						
							
								class MyClass {
									/* ... */
									setA() {
										const me = this;
										function f() {
											me.a += 100;
										}
										f();
									}
								}
								
								const x = new MyClass(5);
								
								x.setA();
								
								console.log(x.getA());
							
						
							
								105
							
						

Arrow-Funktionen

  • Kompakte Schreibweise einer normalen Funktion
  • Bei Einzeilern ist das return nicht notwendig
  • this-Pointer des aktuellen Kontextes wird beibehalten
							
								// Einzeiler
								const f1 = _ => 1;
								const f2 = () => 1;
								const f3 = (x) => x * x;
								const f4 = (x, y) => x + y;
								
								// Mehrzeiler
								const f5 = (x, y) => {
									return x * y;
								};
							
						
							
								class MyClass {
									/* ... */
									setA() {
										const f = () => {
											this.a += 100;
										}
										f();
									}
								}
								
								const x = new MyClass(5);
								
								x.setA();
								
								console.log(x.getA());
							
						
							
								105
							
						

Generator-Funktionen

							
								function* generator() {
									let i = 0;
								
									while(true) {
										yield i;
										i++;
									}
								}
								
								const g = generator();
								
								console.log(g.next().value);
								console.log(g.next().value);
								console.log(g.next().value);
							
						
							
								1
								2
								3
							
						

Generator-Funktionen

							
								function* generator() {
									yield 1;
									yield 2;
									yield 3;
								}
								
								const g = generator();
								
								for (const i of g) {
									console.log(i);
								}
							
						
							
								1
								2
								3
							
						

Nützliche Array-Funktionen

Aufgabe: Schreibe ein Programm, das den Gesamtumsatz aus dem ersten Halbjahr berechnet
							
								const revenues = [
									{month: 0, revenue: 1000},
									{month: 1, revenue: 2000},
									{month: 2, revenue: 3000},
									{month: 3, revenue: 4000},
									{month: 4, revenue: 5000},
									{month: 5, revenue: 6000},
									{month: 6, revenue: 6000},
									{month: 7, revenue: 5000},
									{month: 8, revenue: 4000},
									{month: 9, revenue: 3000},
									{month: 10, revenue: 2000},
									{month: 11, revenue: 1000}
								];
								
								let totalRevenue = 0;
								
								/* ... */
								
								console.log(totalRevenue);
							
						
Lösung
							
								totalRevenue = revenues
									.filter((revenue) => revenue.month < 6)
									.map((revenue) => revenue.amount)
									.reduce((sum, amount) => sum + amount, 0);
							
						
							
								21000
							
						
							
								const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
								
								a.filter((e) => e > 2); // Filterung
								a.map((e) => e * 2); // Umformung
								a.reduce((p, e) => p + e, 0); // Zusammenrechnung / Reduzierung
								a.some((e) => e > 5); // true wenn min. ein Element die Bedingung erfüllt
								a.every((e) => e > 5); // true wenn alle Elemente die Bedingung erfüllen
							
						

Map

  • auch als "assoziatives Array" bezeichnet
  • iterierbare Liste
  • ideal zur Assoziation von z.B. Hashes zu Objekt-Instanzen o.ä.
  • nutzbar ab es6 / es2015
  • löst herkömmliche Objekte als Hash-Map ab
							
								const m = new Map();
								
								m.set("a", 0);
								m.set("a", 1);
								m.set("b", 2);
								
								m.has("c"); // => false
								
								m.delete("b");
								
								console.log(m.get("a")); // => 1
							
						
							
								m.forEach((k, v) => console.log(k, v)) // => "a" 1 ...
								
								for (const entry of m.entries()) {
									console.log(entry); // => ["a", 1] ...
								}
								
								for (const key of m.keys()) {
									console.log(key); // => "a" ...
								}
								
								for (const value of m.values()) {
									console.log(value); // => "1" ...
								}
							
						

Set

  • enthält nur einzigartige Werte
  • iterierbare Liste
  • ideale Struktur, wenn einzelne Werte einzigartig sein sollen
  • nutzbar ab es6 / es2015
  • löst individuelle Lösungen mittels Arrays und Array.prototype.indexOf o.ä. ab
							
								const s = new Set();
								
								s.add("a");
								s.add("a");
								
								s.has("c"); // => false
								
								s.delete("a");
								
								console.log(s.size); // => 0
							
						
							
								s.forEach((k, v) => console.log(k, v)) // => "a" "a" ...
								
								for (const entry of m.entries()) {
									console.log(entry); // => ["a", "a"] ...
								}
								
								for (const key of m.keys()) {
									console.log(key); // => "a"...
								}
								
								for (const value of s.values()) {
									console.log(value); // => "a" ...
								}
							
						

DOM-Events

  • funktionale Ausführung von Code
  • Trigger können Klicks, Scrollen, Tastendrücke, Änderungen der Fenstergröße usw. sein
							
								function doSomething(e) {
									console.log("capture");
								}
								
								function click(e) {
									console.log("bubble");
								}
								
								div.addEventListener("click", doSomething, true);
								
								button.addEventListener("click", click, false /* default */);
							
						
							
								capturing
								bubble
							
						

Aufgabe: Erweitere das vorherige Beispiel so, dass das div-Element einen weiteren Click-Handler bekommt, der den Text "bubble2" anzeigt, nachdem "bubble" in der Konsole geloggt wurde.

Erwartete Ausgabe:

							
								capturing
								bubble
								bubble2
							
						

Lösung:

							
								function doSomething(e) {
									console.log("capture");
								}
								
								// neu
								function doSomething2(e) {
									console.log("bubble2");
								}
								
								function click(e) {
									console.log("bubble");
								}
								
								div.addEventListener("click", doSomething, true);
								div.addEventListener("click", doSomething2, false); // neu
								
								button.addEventListener("click", click, false /* default */);
							
						
Die Bubble-Phase stoppen
							
								function click(e) {
									e.stopPropagation()
								}
							
						

Aufgabe: Ändere das Ergebnis des vorherigen Beispiels, sodass die Bubble-Phase auf Ebene des button-Elements gestoppt wird. Was ändert sich in der Ausgabe?

Lösung:

							
								function doSomething(e) {
									console.log("capture");
								}
								
								function doSomething2(e) {
									console.log("bubble2");
								}
								
								function click(e) {
									e.stopPropagation();
									console.log("bubble");
								}
								
								div.addEventListener("click", doSomething, true);
								div.addEventListener("click", doSomething2, false);
								
								button.addEventListener("click", click, false /* default */);
							
						
Events programatisch triggern:
							
								button.dispatchEvent(new Event("click"));
							
						
Events sind synchron:
							
								doSomethingElse();
								
								button.dispatchEvent(new Event("click"));
								
								doAnotherThing(); // wird erst ausgeführt, wenn zuvor alle Event-Handler fertig sind
							
						

Strings

							
								const empty1 = "";
								const empty1 = '';
								
								const s1 = 'abc';
								const s2 = "abc";
								
								const s3 = 'a"b"c';
								const s4 = "a\"b\"c";
								
								const s5 = 'a\'b\'c';
								const s6 = "a'b'c";
								
								const s7 = `a'b'"c"`;
							
						
Strings zusammenfügen
							
								const age = 10;
								
								// Konkatenation
								const s = "My age is " + number + "!";
								
								// Interpolation
								const s = `My age is ${number}!`;
								
								// => "My age is 10!"
							
						
Template Strings
							
								function sayHello() {
									return "Hello";
								}
								
								let interpolation = `${sayHello()}`;
								
								// => "Hello"
								
								interpolation = `5 + 5 = ${5+5}`;
								
								// => "5 + 5 = 10"
							
						
lit
							
								return html`
hello world!
`
							
								function myTag() {
									console.log(arguments);
								
									// return 5;
									// return true;
									return "abc";
								}
								
								let interpolation = myTag`a ${5} b ${6} c`;
								
								// => "abc"
							
						
							
								Arguments(3) [Array(3), 5, 6]
									0 : (3) ['a ', ' b ', ' c', raw: Array(3)]
									1 : 5
									2 : 6
							
						
Aufgabe

Schreibe ein Tag, welches die Zahlen aus den dynamischen Ausdrücken zusammenrechnet (strings.js)

Lösung
							
								function add(parts, ...numbers) {
									return numbers.reduce((p, c) => p + c, 0);
								}
								
								let sum = add`a ${5} b ${6} c`;
								
								console.log(sum);
								
								// => 11
							
						
Rest-Operator
							
								function add(...numbers) {
									return numbers.reduce((p, c) => p + c, 0);
								}
								
								add(1, 2, 3); // => 6
							
						
							
								const o1 = {a: 1, b: 2};
								const o2 = {x: 5, y: 6, ...o1};
							
						
							
								const a1 = [4, 5, 6];
								const a2 = [1, 2, 3, ...a1];
							
						

Relevante Browser-APIs

window
							
								window.innerWidth; // innere Auflösung des sichtbaren Fensters
								window.innerHeight;
								window.outerWidth; // äußere Auflösung des Browserfensters inklusive Adresszeile usw.
								window.outerHeight;
								
								window.addEventListener(); // Events an das äußerste "Dach"-Element hängen
							
						
location
							
								location.href; // aktuelle URL
								location.host; // aktuelle Domain, z.B. "www.google.de"
								location.protocol; // aktuelles Protokoll, z.B. "https:"
							
						
Navigator
							
								navigator.language; // bevorzugte Browsersprache, z.B. "de"
								navigator.userAgent; // z.B. "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) [...]"
								navigator
									.mediaDevices
									.getDisplayMedia(); // Fordere Erlaubis zum Streamen von Anwendungen oder Bildschirmen an
							
						
Document
							
								document.body; // <body>-Element der Seite
								document.activeElement; // aktives Element der Seite, welche gerade den Fokus besitzt
								document.cookie; // für die aktuelle Seite gesetzte Cookies
								document.execCommand("copy"); // Texte in die Zwischenablage zu schreiben
								document.createElement("div"); // erzeuge neues DOM-Element mit angegebenen Tag
							
						
LocalStorage
							
								// auf 5MB Daten pro Domain begrenzt
								localStorage.getItem(key); // Element holen
								localStorage.setItem(key, value); // Element setzen
								localStorage.removeItem(key); // Element entfernen
							
						

CSS

Warum CSS?

  • Erhöhte Zugänglichkeit des Inhalts
  • Wiederverwendbarkeit

Syntax

							
								selector [, selector2, selector3, ...] {
									property1: value;
									property2: value;
								}
							
						

Beispiel

							
								html, body {
									width: 100%;
									height: 100%;
									margin: 0;
								}
							
						

Selektoren

							
								/* Elemente */
								html, body {...}
								
								/* IDs */
								#my-id {...}
								
								/* Klassen */
								.my-class {...}
								
								/* Pseudo-Klassen */
								div:hover {...}
								
								/* Attribute */
								div[my-attribut="abc"] {...}
								
								/* Kombinationen */
								div.my-class#my-id {...}
							
						

Variablen

							
								html {
									--my-background-color: #abcdef;
								}
								
								html div {
									background-color: var(--my-background-color, white);
								}
							
						

Das display-Attribut

block

  • untereinander angeordnet
  • nehmen sich die volle Breite
  • width und height möglich

inline

  • Standard bei Custom Elements
  • nur so groß wie der content
  • width und height nicht möglich
  • nebeneinander angeordnet

inline-block

  • Mischform von block und inline
  • nur so groß wie der content
  • width und height möglich
  • nebeneinander angeordnet

none

  • unsichtbar
  • Platz wird nicht länger beansprucht, so als wenn das Element nicht im DOM wäre

Das position-Attribut

static

  • Standard
  • Element wird am Ort der Verankerung im HTML dargestellt

relative

  • Verschiebung zur eigentlichen static-Position, z.B. mittels left: 20px

absolut

  • relative Verschiebung zum nächsten nicht-static-Parent (sonst window)

Aufgabe

Implementiere folgendes Beispiel-Menü

Lösung

							
								.menu-item {
									display: inline-block;

									position: relative;
								}

								.menu-item .children {
									display: none;

									position: absolute;
								}

								.menu-item:hover .children {
									display: block;
								}
							
						
							
								
							
						

Viewport

							
								<!DOCTYPE html>
								<html>
									<head>
										<meta charset="utf-8"></meta>
										<meta
											name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
										>
									</head>
									<body>
										...
									</body>
								</html>
							
						

Ein Muss für alle responsiven Seiten

							
								npm install -g http-server
								cd pfad/zum/projekt
								http-server --port 8080
							
						

Einheiten

							
								div {
									width: 1px; /* Pixel */
									width: 1pt; /* Points */
									width: 1em; /* Breite des "M" in der Schriftgröße des Elements */
									width: 1rem; /* Breite des "M" in der Schriftgröße des HTML-Elements */
									width: 1vw; /* Prozent der View Width */
									width: 1%; /* Prozent des Elternelements */
									
									width: 5fr; /* Fraction (nur im CSS Grid verfügbar) */
								}
							
						

Aufgabe

Größeneinheiten in einer HTML-Datei ausprobieren

CSS Flexbox

							
								.container {
									display: flex; /* soll ein flexibler Container sein */
									display: inline-flex;
								
									flex-direction: column; /* Kinder spaltenweise anordnen */
									flex-direction: row; /* Kinder zeilenweise anordnen */
								
									align-items: center;
									justify-content: center;
								
									gap: 20px; /* Abstände zwischen den Achsen */
								}
								
								.child {
									flex-grow: 1; /* Anteiliger Platz, wenn mehr als nötig da ist */
									flex-shrink: 1; /* Anteiliger Platz, wenn weniger als nötig da ist */
									flex-basis: 20px; /* Grundlage zur Berechnung der finalen Größe */
									order: 1; /* Erlaubt die Darstellung in anderer Reihenfolge als durch das HTML vorgegeben */
								}
							
						

Aufgabe

Mit CSS Flexbox rumspielen

CSS Grid

Terminologie

							
								.grid {
									display: grid; /* inline-grid; */
					
									grid-template-columns: 1fr 1fr 1fr;
									grid-template-rows: 1fr 1fr;
									
									gap: 5px 10px;
								}
							
						
							
								<div class="grid">
									<span>A</span>
									<span>B</span>
									<span>C</span>
									<span>D</span>
									<span>E</span>
									<span>F</span>
								</div>
							
						
							
								.grid {
									grid-template-columns: [left] 1fr [column1] 1fr [column2] 1fr [right];
									grid-template-rows: [top] 1fr [row1] 1fr [bottom];
								}
								
								.a {
									grid-column: 2 / span 2;
									grid-row: 1;
								}
								
								.b {
									grid-column: left / column2;
									grid-row: 2;
								}
							
						
							
								<div class="grid">
									<span class="a">A</span>
									<span class="b">B</span>
								</div>
							
						
							
								.grid {
									grid-template-areas:
										"header header header"
										"content content content";
								}
					
								.a {
									grid-area: header;
								}
					
								.b {
									grid-area: 2 / 1 / 2 / 3; /* start-row / start-column / end-row / end-column */
										/* alternativ auch Namen der Lines möglich */
								}
							
						

Aufgabe

Implementiere folgendes Design:

Lösung

							
								.grid {
									display: grid;
									
									grid-template-rows: 1fr 5fr 1fr;
									
									gap: 2px;
					
									grid-template-areas:
										"navigation header header header"
										"navigation content content content"
										"footer footer footer footer"
								}
					
								.navigation {
									grid-area: navigation;
								}
					
								.header {
									grid-area: header;
								}
					
								.content {
									grid-area: content;
								}
					
								.footer {
									grid-area: footer;
								}
							
						
							
								<div class="grid">
									<div class="area navigation">Navigation</div>
									<div class="area header">Header</div>
									<div class="area content">Content</div>
									<div class="area footer">Footer</div>
								</div>
							
						

Platzierug des Inhalts

							
								.grid {
									align-items: center;
									justify-items: center;
								}
							
						

Media Queries

  • "Medienabfragen"
  • Styles auf Basis von Eigenschaften des Ausgabemediums
  • Große Bildschirme, kleine Bildschirme, Printmedien...
  • Hinweis: Sollten immer als letztes (unten) in einem Stylesheet sein

Syntax

							
								@media screen and (min-width: 640px) and (max-width: 1024px) {
									.navigation {
										display: none;
									}
								}
							
						

Bedeutung: Wenn die Seite auf einem Display angezeigt wird, welches zwischen 640px und 1024px in der Breite anzeigen kann, dann blende die Navigation aus

Werte für die Ausgabemedien (optional, Standard ist "all")

  • all
  • screen
  • print

Werte für die Eigenschaften mit Typ und Erklärung (kleiner Auszug)

  • width (Number, Breite)
  • height (Number, Höhe)
  • orientation (portrait | landscape, Quer- oder Hochformat)
  • hover (none | hover, Hat Zeiger zum Hovern)
  • forced-colors (none | active, erzwungene Farbpalette z.B. bei Sehbehinderung)

Aufgabe

Entwerfe eine kleine Seite mit @media und zeige ein beliebiges Element ab einer bestimmten Höhe oder Breite an (die Größe des Displays kann dynamisch mit den Entwicklertools im Gerätesimulator geändert werden)

Aufgabe

Passe die Vorlage "media-queries-responsive-layout.html" mit @media-Queries so an, dass das Menü links ab einer maximalen Breite von 480 Pixel und kleiner nach links oben in die Ecke minimiert wird und nur per Hover wieder 50% Breite und 100% Höhe bekommt

Lösung

							
								@media screen and (max-width: 480px) {
									.menu {
										grid-area: unset;
					
										position: absolute;
									}
									
									.menu:hover {
										height: 100%;
										width: 50%;
									}
					
									.grid {
										grid-template-columns: 0fr 5fr;
									}
								}
							
						

Transform

  • rein optisch, d.h. "physische" Größe bleibt erhalten
  • eine Art "Shader" auf Grafikebene
  • Effekte wie Drehung, Vergrößerung, Verschiebung usw.

Syntax

							
								.foo {
									transform: translateX(20px) translateY(20px);
									transform-origin: left;
								}
							
						

Mögliche Effekte

  • translate
  • rotate
  • scale
  • scew

Die meisten besitzen zusätzlich spezifischere X-, Y- oder 3D-Versionen

Aufgabe

Probiere die genannten Transform-Effekte in einer beliebigen Seite aus

Transition

  • Elemente können "weich" verändert werden
  • Mögliche Attribute, deren Änderungen animiert werden können sind z.B. width, height, transform, color und viele weitere
  • Das Attribut display lässt sich jedoch nicht animieren

Syntax

							
								.foo {
									width: 100%;
									background-color: red;
									/* transition: [Attribut] [Dauer] [Zeitfunktion] */
									transition: width 1s ease-out;
								}
					
								.foo:hover {
									width: 50%;
								}
							
						

Beispiel

Animation

  • Es können zusätzlich eigene Animationen entworfen werden
  • Definition per Keyframes

Syntax

							
								.foo {
								  animation-duration: 3s;
								  animation-name: slidein;
								}
								
								@keyframes slidein {
								  from {
									margin-left: 100%;
									width: 300%;
								  }
								
								  to {
									margin-left: 0%;
									width: 100%;
								  }
								}
							
						

Alternative Syntax

							
								.foo {
								  /* ... */
								}
								
								@keyframes slidein {
								  1% {
									margin-left: 100%;
								  }
								
								  100% {
									margin-left: 0%;
								  }
								}
							
						

Beispiel

3D Transform

  • Neben den gewohnten X- und Y-Achsen (Breite und Höhe) gibt es auch noch die Z-Achse
  • Bewegung entlang der Sichtachse, d.h. zum bzw. vom Betrachter weg
  • Perspektivische Effekte möglich

Beispiel

Bedingungen / Optionen

							
								.container {
									perspective: 1000px; /* "Entfernung" des Betrachters */
								
									transform-style: preserve-3d; /* oder "flat" */
								}
								
								.element {
									backface-visibility: hidden; /* oder "visible" */
								}
							
						

Beispiel

Aufgabe

Implementiere einen drehenden Würfel (Vorlage "templates/008-css/css-3d-cube.html")

Web Components

Was sind Web Components?

Vorteile

  • Entwicklung eigener HTML-Elemente, welche sich nahtlos neben nativen Elementen nutzen lassen
  • Hohe Wiederverwendbarkeit in mehreren Projekten
  • Keine Libraries oder Frameworks notwendig, um eigene Elemente zu entwickeln
  • Kapselung des Scopes sowohl von JS (Variablen...) als auch CSS (id, class...)

Ansätze in der Vergangenheit und Alternativen heute

jQuery

https://api.jqueryui.com/accordion/#entry-examples
							
								
							
						

angular

							
								
							
						

Aufbau einer Web Component

							
								
							
						
							
								
							
						

Auf Attribute reagieren

							
								
							
						

Aufgabe

Lasse die eigene Komponente "hello-sayer" auf eine Änderung des "name"-Attributs reagieren, indem der dort eingetragene Name in der Komponente angezeigt wird (Beispiel: name="Joe" => Hello Joe!)

(/examples/templates/web-components/hello-sayer.html)