Der Webbrowser ist die ursprüngliche und nach wie vor wichtigste Laufzeitumgebung für JavaScript. Um JavaScript-Code effektiv zu entwickeln, ist ein tiefes Verständnis der Browser-Umgebung unerlässlich – von der JavaScript-Engine über das Document Object Model (DOM) bis hin zu den zahlreichen Web-APIs.
Eine JavaScript-Engine ist der Kern jedes Browsers, der für die Ausführung von JavaScript-Code verantwortlich ist. Die wichtigsten Engines sind:
Diese Engines übersetzen JavaScript-Code in Maschinencode und führen ihn aus. Der Prozess umfasst typischerweise:
// Dieses einfache Beispiel durchläuft alle oben genannten Phasen
function add(a, b) {
return a + b;
}
// Bei wiederholten Aufrufen kann die JIT-Kompilierung einsetzen
for (let i = 0; i < 10000; i++) {
add(i, i + 1);
}Der Browser-Prozess ist in der Regel multi-threaded, wobei JavaScript auf dem Hauptthread (UI-Thread) ausgeführt wird. Dies erklärt, warum rechenintensive JavaScript-Operationen die Benutzeroberfläche blockieren können.
Das Browser-Objektmodell bietet JavaScript Zugriff auf die
Funktionalitäten des Browsers selbst und ist nicht standardisiert. Das
globale window-Objekt ist der Einstiegspunkt zum BOM und
enthält unter anderem:
Das window-Objekt ist das globale Objekt im Browser und
bietet Zugriff auf:
// Fenstergröße und -position
console.log(window.innerWidth, window.innerHeight);
window.moveTo(100, 100);
window.resizeTo(800, 600);
// Browserfunktionen
window.alert('Hinweis');
window.confirm('Möchten Sie fortfahren?');
window.prompt('Bitte geben Sie Ihren Namen ein');
// Timing-Funktionen
const timerId = window.setTimeout(() => {
console.log('Nach 1 Sekunde ausgeführt');
}, 1000);
window.clearTimeout(timerId);
const intervalId = window.setInterval(() => {
console.log('Wird alle 2 Sekunden ausgeführt');
}, 2000);
window.clearInterval(intervalId);Mit window.location lässt sich die URL des Browsers
manipulieren:
// Aktuelle URL-Komponenten
console.log(location.href); // Vollständige URL
console.log(location.protocol); // "http:" oder "https:"
console.log(location.host); // Hostname mit Port
console.log(location.pathname); // Pfadteil der URL
console.log(location.search); // Query-String
console.log(location.hash); // URL-Fragment
// Navigation auslösen
location.href = 'https://example.com'; // Vollständige Navigation
location.assign('https://example.com'); // Äquivalent zu href-Zuweisung
location.replace('https://example.com'); // Ersetzt aktuellen Eintrag im Verlauf
location.reload(); // Seite neu ladenwindow.history ermöglicht die Navigation im
Browser-Verlauf:
// Im Verlauf navigieren
history.back(); // Eine Seite zurück
history.forward(); // Eine Seite vor
history.go(-2); // Zwei Seiten zurück
// History API für SPAs (Single Page Applications)
history.pushState({page: 1}, "Titel", "/neuer-pfad"); // Verlaufseintrag hinzufügen
history.replaceState({page: 1}, "Titel", "/pfad"); // Aktuellen Eintrag ersetzen
// Bei Navigation zurück/vor wird das popstate-Event ausgelöst
window.addEventListener('popstate', (event) => {
console.log('Navigation im Verlauf', event.state);
});window.navigator bietet Informationen über den Browser
und das System:
// Browser- und Systeminformationen
console.log(navigator.userAgent); // Browser-Identifikation
console.log(navigator.platform); // Betriebssystem
console.log(navigator.language); // Bevorzugte Sprache
console.log(navigator.onLine); // Online-Status
// Zugriff auf Hardware (mit Berechtigungen)
navigator.geolocation.getCurrentPosition((position) => {
console.log(position.coords.latitude, position.coords.longitude);
});Das DOM ist eine programmatische Repräsentation der HTML-Struktur einer Webseite. Es ermöglicht JavaScript, auf die Struktur, den Inhalt und das Styling der Webseite zuzugreifen und diese zu manipulieren.
Das DOM stellt ein HTML-Dokument als Baumstruktur dar, beginnend mit
dem document-Objekt:
// Zugriff auf Dokumentinformationen
console.log(document.title); // Titel der Seite
console.log(document.URL); // URL des Dokuments
console.log(document.doctype); // Doctype-Information
console.log(document.documentElement); // Das <html>-ElementNodes im DOM-Baum können auf verschiedene Weise durchlaufen werden:
// Grundlegende Navigation
const body = document.body;
const firstChild = body.firstChild;
const lastChild = body.lastChild;
const parent = firstChild.parentNode;
const nextSibling = firstChild.nextSibling;
const previousSibling = lastChild.previousSibling;
// Nur Element-Nodes
const firstElement = body.firstElementChild;
const lastElement = body.lastElementChild;
const nextElement = firstElement.nextElementSibling;
const previousElement = lastElement.previousElementSibling;
const parentElement = firstElement.parentElement;JavaScript kann den DOM-Baum dynamisch verändern:
// Elemente erstellen
const div = document.createElement('div');
div.textContent = 'Neues Element';
div.className = 'container';
div.id = 'main-container';
// Attribute manipulieren
div.setAttribute('data-created', 'dynamically');
const hasAttr = div.hasAttribute('data-created');
const attrValue = div.getAttribute('data-created');
div.removeAttribute('data-created');
// Elemente in den DOM einfügen
document.body.appendChild(div); // Als letztes Kind anfügen
document.body.insertBefore(div, firstChild); // Vor einem bestimmten Element einfügen
// Moderne Methoden (bessere Browser-Unterstützung prüfen)
parentElement.append(div); // Als letztes Kind anfügen
parentElement.prepend(div); // Als erstes Kind anfügen
existingElement.before(div); // Vor einem Element einfügen
existingElement.after(div); // Nach einem Element einfügen
existingElement.replaceWith(div); // Element ersetzen
// Elemente entfernen
element.remove(); // Moderner Ansatz
parentElement.removeChild(element); // Älterer, universeller Ansatz
// Inhalt manipulieren
element.textContent = 'Nur Text'; // Nur Text, ohne HTML-Parsing
element.innerHTML = '<strong>HTML</strong>'; // Mit HTML-Parsing (potenziell unsicher!)
element.innerText = 'Sichtbarer Text'; // Nur sichtbarer Text (vermeiden)Es gibt verschiedene Methoden, um Elemente im DOM zu finden:
// Direkte Selektoren (liefern einzelne Elemente)
const elementById = document.getElementById('main-heading');
const firstByTag = document.querySelector('h1');
const firstByClass = document.querySelector('.container');
const firstByAttribute = document.querySelector('[data-role="button"]');
// Selektoren für mehrere Elemente (liefern NodeList oder HTMLCollection)
const allParagraphs = document.getElementsByTagName('p'); // Live HTMLCollection
const allButtons = document.getElementsByClassName('btn'); // Live HTMLCollection
const allLinks = document.querySelectorAll('a.external-link'); // Statische NodeList
// Live vs. statisch ist ein wichtiger Unterschied:
const paragraphs = document.getElementsByTagName('p'); // Live
const paragraphsQuery = document.querySelectorAll('p'); // Statisch
// Wenn ein neuer Paragraph hinzugefügt wird:
document.body.appendChild(document.createElement('p'));
console.log(paragraphs.length); // Erhöht um 1 (live)
console.log(paragraphsQuery.length); // Unverändert (statisch)Der Browser ist ereignisgesteuert. JavaScript kann auf Benutzerinteraktionen und andere Events reagieren:
function handleClick(event) {
console.log('Button wurde geklickt!', event);
}
// Event-Listener hinzufügen
button.addEventListener('click', handleClick);
// Mit zusätzlichen Optionen
button.addEventListener('click', handleClick, {
once: true, // Listener wird nach einmaliger Ausführung entfernt
capture: true, // Event wird in der Capturing-Phase behandelt
passive: true // Performance-Optimierung (verhindert preventDefault)
});
// Event-Listener entfernen
button.removeEventListener('click', handleClick);Das Event-Objekt enthält detaillierte Informationen über das ausgelöste Event:
element.addEventListener('click', (event) => {
console.log(event.type); // Art des Events ("click")
console.log(event.target); // Element, das das Event ausgelöst hat
console.log(event.currentTarget); // Element, an dem der Listener registriert ist
console.log(event.clientX, event.clientY); // Mausposition
// Standardverhalten verhindern
event.preventDefault();
// Event-Propagation stoppen
event.stopPropagation();
});Events durchlaufen drei Phasen:
// HTML-Struktur:
// <div id="outer">
// <div id="middle">
// <button id="inner">Klick mich</button>
// </div>
// </div>
document.getElementById('outer').addEventListener('click', (e) => {
console.log('Outer div geklickt (Bubbling)');
}, false); // false = Bubbling (Standard)
document.getElementById('middle').addEventListener('click', (e) => {
console.log('Middle div geklickt (Bubbling)');
}, false);
document.getElementById('inner').addEventListener('click', (e) => {
console.log('Button geklickt (Bubbling)');
}, false);
// Capturing-Phase-Listener
document.getElementById('outer').addEventListener('click', (e) => {
console.log('Outer div geklickt (Capturing)');
}, true); // true = Capturing
// Bei Klick auf den Button ist die Ausgabereihenfolge:
// 1. "Outer div geklickt (Capturing)" - Capturing-Phase
// 2. "Button geklickt (Bubbling)" - Target erreicht
// 3. "Middle div geklickt (Bubbling)" - Bubbling-Phase
// 4. "Outer div geklickt (Bubbling)" - Bubbling-PhaseEvent-Delegation ist ein leistungsfähiges Muster, bei dem Events an übergeordneten Elementen behandelt werden:
// Statt vieler einzelner Listener auf jedem Listenelement...
document.querySelector('ul').addEventListener('click', (event) => {
// Prüfen, ob ein Listenelement geklickt wurde
if (event.target.tagName === 'LI') {
console.log('Listenelement geklickt:', event.target.textContent);
}
});Dies ist besonders nützlich für dynamisch erzeugte Elemente und spart Ressourcen.
Moderne Browser bieten zahlreiche APIs für erweiterte Funktionalitäten:
Die Fetch API bietet eine moderne Schnittstelle für HTTP-Anfragen:
// GET-Anfrage
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Netzwerkantwort war nicht ok');
}
return response.json(); // Oder response.text(), response.blob() etc.
})
.then(data => {
console.log('Daten empfangen:', data);
})
.catch(error => {
console.error('Fetch-Fehler:', error);
});
// POST-Anfrage mit zusätzlichen Optionen
fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Max Mustermann',
email: 'max@example.com'
})
})
.then(response => response.json())
.then(data => console.log('Antwort:', data));
// Mit async/await (wird in späteren Kapiteln detaillierter behandelt)
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Netzwerkfehler');
const data = await response.json();
return data;
} catch (error) {
console.error('Fehler beim Abrufen der Daten:', error);
}
}Für die clientseitige Datenspeicherung:
// localStorage (persistent)
localStorage.setItem('username', 'Max');
const username = localStorage.getItem('username');
localStorage.removeItem('username');
localStorage.clear(); // Alles löschen
// sessionStorage (nur für die Dauer der Sitzung)
sessionStorage.setItem('temporaryData', JSON.stringify({id: 123}));
const data = JSON.parse(sessionStorage.getItem('temporaryData'));Diese APIs werden in späteren Kapiteln detaillierter behandelt.
JavaScript im Browser unterliegt strengen Sicherheitsrichtlinien:
Die SOP schränkt den Zugriff auf Ressourcen aus anderen Herkunftsquellen ein:
CORS ist ein Mechanismus, der kontrollierte Cross-Origin-Anfragen ermöglicht:
// Server muss entsprechende CORS-Header senden
fetch('https://api.other-domain.com/data', {
// Credentials nur bei expliziter Erlaubnis durch den Server
credentials: 'include'
});CSP ist eine zusätzliche Sicherheitsebene, die XSS-Angriffe einschränkt:
<!-- Via HTTP-Header oder Meta-Tag -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' trusted-scripts.com">JavaScript kann die Rendering-Performance erheblich beeinflussen:
Der Browser führt mehrere Schritte durch, um eine Seite darzustellen:
JavaScript kann diesen Prozess in jeder Phase unterbrechen oder ändern.
// Ineffizient: Verursacht mehrere Reflows
element.style.width = '100px';
element.style.height = '200px';
element.style.margin = '10px';
// Effizienter: Ein einziger Reflow
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';
// Oder
element.className = 'new-style';Für flüssige Animationen und visuelle Updates:
function animate() {
// Visuelle Aktualisierungen hier
element.style.transform = `translateX(${position}px)`;
position += 5;
// Nächsten Frame anfordern
requestAnimationFrame(animate);
}
// Animation starten
requestAnimationFrame(animate);Moderne Webanwendungen können auch offline funktionieren:
Service Workers sind JavaScript-Worker, die als Proxy zwischen Browser und Netzwerk agieren:
// Service Worker registrieren
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registriert:', registration.scope);
})
.catch(error => {
console.error('Service Worker Registrierung fehlgeschlagen:', error);
});
}
// In der sw.js-Datei:
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles.css',
'/app.js'
]);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});Diese Themen werden im Kapitel zu Progressive Web Apps vertieft.