JavaScript-Tutorium:Browser: Unterschied zwischen den Versionen
Zeile 101: | Zeile 101: | ||
</source> | </source> | ||
Frames können entweder durch das entsprechende HTML oder aber auch programmatisch erzeugt werden. | Frames können entweder durch das entsprechende HTML oder aber auch wie im Code gezeigt programmatisch erzeugt werden. | ||
==Browser-Schnittstellen== | ==Browser-Schnittstellen== |
Version vom 1. November 2014, 18:11 Uhr
Ziel
Ziel dieses Tutoriums ist es die Grundlagen und Besonderheiten bei der JavaScript-Entwicklung im Browser zu vermitteln. Zur Veranschaulichung sind viele Code-Beispiele gegeben.
Einführung
Native vs. Host
In einer JavaScript-Umgebung existieren immer zwei unterschiedliche Arten von Objekten und Funktionen. Native Objekte werden von der JavaScript-Engine zur Verfügung gestellt und sind durch den ECMAScript-Standard abgedeckt. Host-Objekte sind hingegen nicht zwingend standardisiert und werden von der Umgebung bereitgestellt. Im Browser handelt es sich bei Host-Objekten um Schnittstellen für den Zugriff auf den Browser selbst und externe Geräte.
HTML5-Standard
Das bedeuteut, dass obwohl die Sprache selbst dem ECMAScript-Standard folgt, musste man bis vor wenigen Jahren noch davon ausgehen dass sich die verfügbaren APIs je nach Browser sehr stark unterscheiden können. Durch die steigende Entwicklungsgeschwindigkeit der Browser-Hersteller und den allgemein akzeptierten HTML5-Standard verbessert sich diese Situation jedoch zunehmend.
Heutzutage kann man davon ausgehen, dass jeweils die neuesten Versionen von Firefox, Chrome und Internet Explorer die DOM-API und viele der neu verfassten HTML5-APIs bereits standardkonform unterstützen.
Inline Code vs. externe Dateien
Eine im Browser angezeigte Web-Seite besteht meist aus HTML, CSS und JavaScript, wobei nur HTML zwingend verlangt ist. Der JavaScript-Code kümmert sich normalerweise um die dynamischen Aspekte, wie Interaktionen und das Nachladen von Inhalten.
Beim Besuch einer Website lädt der Browser das HTML-Dokument unter der angegeben URL herunter und evaluiert es. Das Dokument enthält dann wiederum optional Verweise auf CSS-Dateien, Bilder, JavaScript-Dateien und andere Medien. Neben Verweisen auf externen JavaScript-Code kann das Dokument auch Inline-Code-Blöcke enthalten. Ob es sich um externe Dateien oder Inline-Code handelt wirkt sich auf die Ladezeiten und das Caching des Browser aus, die enthaltenen Anweisungen und Ausdrücke werden jedoch nicht davon beeinflusst.
'Anmerkung: Die Reihenfolge der eingebunden Code-Blöcke und angegebenen Dateiverweise ist allerdings wichtig, da diese ebenso die Reihenfolge der Code-Ausführung darstellt.
JavaScript-Kontext
Für jede geladene Web-Seite, unabhängig davon ob sie im separaten Fenster oder als ein weiterer Tab angezeigt wird, erzeugt der Browser einen separaten und isolierten JavaScript-Kontext, in dem der geladene Code ausgeführt wird. Das bedeutet dass alle Objekte und Funktionen nur so lange am Leben sind bis die Seite neu geladen oder verlassen wird. Ein Zugriff auf andere Seiten in anderen Fenstern oder Tabs ist aus Sicherheitsgründen nicht erlaubt.
Window
Das globale Objekt window
referenziert alle im Browser verfügbaren Objekte und Werte als Eigenschaften (native und host).
console.log(window.Object);
console.log(window.XMLHttpRequest);
Für den Zugriff auf globale Eigenschaften ist es nicht notwendig explizit window
zu schreiben.
console.log(Date == window.Date);
console.log(new Date());
Alle nicht innerhalb von Funktionen deklarierten Variablen werden automatisch an das globale Objekt angehängt.
var globaleVariable = 'global';
var globaleFunktion = function() {};
console.log(window.globaleVariable);
console.log(window.globaleFunktion);
Modifikation von Eigenschaften
Da es sich um ein normales Objekt handelt können die meisten Eigenschaften modifiziert und auch gelöscht werden.
Anmerkung: Veränderungen von globalen Eigenschaften sind nicht sinnvoll und können schwerwiegende Fehler verursachen.
window.Object = {};
var objekt = new Object();
delete window.Array;
var array = new Array();
Das window
-Objekt wird vom Browser mithilfe der Konstruktorfunktion Window
erzeugt, welche auch global verfügbar ist.
console.log(window.constructor == window.Window);
Popups und Frames
Neben dem globalen Fenster-Objekt kann eine Web-Seite beliebig viele weitere window
-Objekte enthalten.
Dabei handelt es sich dann entweder um durch Code erzeugte Popups oder aber um Frames (besser bekannt als iframes).
window.open('https://www.google.com', 'Google im Popup', 'width=400,height=300,resizable=yes');
Frames können entweder durch das entsprechende HTML oder aber auch wie im Code gezeigt programmatisch erzeugt werden.
Browser-Schnittstellen
Abgesehen von den nativen Objekten notwendigen Objekte enthält window
wichtige Browser- und Geräte-Schinttstellen.
Dazu zählen zum Beispiel Funktionen um Dialoge im Browser zu öffnen oder aber um Game-Controller zu verwenden.
var name = window.prompt('Wie ist Ihr Name?');
window.alert('Hello ' + name);
window.alert('Die aktuelle Seite ist ' + window.innerWidth + 'px breit');
console.log(window.Gamepad);
Eine komplette Liste der verfügbaren Eigenschaften finden Sie auf dieser MDN-Seite. In den folgenden Abschnitten werden exemplarisch ein paar wichtige Objekte im Detail beschrieben.
Beispiel für Browser-Schnittstellen
Location
location
ist ein globales Objekt welches die aktuelle URL und ihre Bestandteile enthält und diese auch verändern kann.
Das Objekt wird analog zum window
-Objekt mithilfe der Konstruktorfunktion Location
erstellt.
console.log(window.location.constructor == window.Location);
Abfragen der URL
Die Eigenschaft href
enhtält die gesamte aktuelle URL. Alternativ kann auch location.toString()
genutzt werden.
console.log(location.href);
console.log(location.toString()
Um einzelne Bestandteile der URL ermitteln zu können werden viele separate Eigenschaften bereitgestellt.
console.log(location.protocol);
console.log(location.host);
console.log(location.port);
console.log(location.pathname);
console.log(location.hash);
Verändern der URL
Diese Eigenschaften können nicht nur lesend genutzt werden, sondern auch um Änderungen an der aktuellen URL vorzunehmen.
Mit der Ausname von location.hash
führen Änderungen dieser Eigenschaften zum Verlassen der Seite.
location.protocol = 'http';
location.port = '8080';
location.hash = '#anker';
location.href = 'https://www.google.com';
Des Weiteren gibt es wenige Funktionen um die URL zu modifizieren oder aber die Seite neuzuladen:</source>
location.assign('https://www.google.com');
location.reload(true);
History
Das globale history
-Objekt ermöglicht den programmatischen Zugriff auf den Browser-Verlauf.
Es werden Funktionen bereitgestellt um im Verlauf beliebig viele Schritte zurück und vorwärts zu gehen.
history.back();
history.forward();
history.go(-3);
history.go(3);
Anmerkung: Aus Sicherheitsgründen ist es nicht erlaubt auf Informationen einzelner Einträge im Verlauf zuzugreifen.
Für detaillierte Informationen über den aktuellen Client bzw. Browser existiert das navigator
-Objekt.
Die meistgenutzte Eigenschaft navigator.userAgent
ermöglicht das Feststellen des Browser-Herstellers.
console.log(navigator.userAgent);
console.log(navigator.battery);
Document Object Model
Im Browser können einem Fenster beliebig viele Dokumentenobjekte zugeordnet werden, mindestens aber ein Hauptdokument.
Das automatisch erstellte Hauptdokument wird über die globale Variable document
zur Verfügung gestellt.
Die Schnittstelle für den Dokumentenzugriff wird durch das Document Object Model spezifiziert.
Objektmodell
Ein Document
-Objekt repräsentiert ein vom Browser interpretiertes HTML-Dokument als Objektmodell mit einer Baumstruktur.
Dabei werden Tags, Texte und Kommentare in Knoten umgewandelt, wobei Tag-Knoten wiederum Unterknoten haben können.
<html>
<head>
<title>Titel</title>
</head>
<body>
<p>
Text
<!-- Kommentar -->
</p>
<input type="text" value="Eingabe">
</body>
</html>
Das oben dargestellte Markup wird bei der Interpretation durch den Browser in folgendes Objektmodell übersetzt:
- Ein
html
-Knoten mit den Unterknotenhead
undbody
- Ein
head
-Knoten mit dem Unterknotentitle
- Ein
title
-Knoten mit einem Text-Unterknoten - Ein
body
-Knoten mit den Unterknotenp
undinput
- Ein
p
-Knoten mit einem Text-Unterknoten und einem Kommentar-Unterknoten - Ein
input
-Knoten
Tipp: Achtet man bei HTML auf eine korrekte Einrückung so ist das resultierende Objektmodell meist klar erkennbar.
Markup-Korrekturen
Dabei ist wichtig zu beachten, dass es sich bei dem Objektmodell nicht um das originale Markup handelt. Der Unterschied wird besonders deutlich wenn man das Resultat von folgendem invalidem HTML untersucht.
<html>
<head>
<title>Titel</title>
</head>
<body>
<table>
<tr>
<td>Tabelle</td>
</tr>
</table>
</body>
</html>
Das Objektmodell fügt zwischen dem table
- und dem tr
-Knoten einen tbody
-Knoten ein damit das HTML valide wird.
Tipp: Im Browser kann man das Objektmodell einer Seite mit dem (DOM) Inspector der Entwicklerwerkzeuge betrachten.
Aufbau von Dokumenten
Node-Objekte
Jeder Knoten innerhalb eines Dokumentenobjekts ist direkt oder indirekt abgeleitet vom Node
-Konstruktor/-Prototypen.
Mit Ausnahme des Dokumentenobjekts selbst wird jedem Knoten ein Dokument zugeordnet durch die ownerDocument
-Eigenschaft.
Des Weiteren hat jeder Knoten einen bestimmten numerischen Typen, welcher in der Eigenschaft nodeType
gespeichert wird.
Die folgende Liste enthält die gängigsten Typen von Dokumentenknoten denen man bei der Arbeit mit Dokumenten begegnet:
- 1: ELEMENT_NODE
- 3: TEXT_NODE
- 8: COMMENT_NODE
- 9: DOCUMENT_NODE
Eine vollständige Referenz der Eigenschaften von Node-Objekten finden Sie auf dieser MDN-Seite.
Document-Objekte
Ein Dokument wird mit dem Document
-Konstruktor erzeugt und ist selbst keinem Dokument, jedoch einem Fenster zugeordnet.
console.log(document.defaultView);
console.log(window == document.defaultView);
Das Dokument selbst repräsentiert kein HTML-Element, verweist aber auf das html
-Element mit der Eigenschaft documentElement
.
console.log(document.documentElement);
Mithilfe der Funktionen eines Dokuments ist es möglich neue Element
-Objekte zu erstellen, einzufügen und diese zu suchen.
Element-Objekte
Jedes HTML-Tag wird in einen Knoten übersetzt welcher mit dem Element
-Konstruktor erzeugt wird.
Element-Objekte besitzen Schnittstellen um auf Eigenschaften eines HTML-Tags zuzugreifen und diese zu modifizieren.
console.log(document.documentElement.tagName);
console.log(document.documentElement.innerHTML);
Ebenso wie Dokumente können Element-Objekten weitere Node-Objekte als Unterknoten angehängt und nach ihnen gesucht werden.
Comment-Objekte
HTML-Kommentare werden in Comment-Objekte umgewandelt welche mit dem Comment
-Konstruktor erzeugt werden.
Text-Objekte
Texte werden in Text
-Objekte umgewandelt, wobei auf diese auch durch übergeordnete Elemente zugegriffen werden kann.
Normalerweise wird bei der Anwendungsprogrammierung mit JavaScript nicht direkt mit Text-Knoten gearbeitet.
Erstellen von Elementen
Neue Element-Objekte werden über die Schnittstelle eines Document
-Objekts unter der Angabe eines Tag-Namens erzeugt.
var absatz = document.createElement('p');
console.log(absatz.tagName);
console.log(absatz.nodeType);
Bei der Erstellung der Elemente wird das augerufene Dokument als zugehöriges Dokument dem neu erstellten Objekt zugewiesen.
var block = document.createElement('div');
console.log(block.ownerDocument);
Da die Schnittstelle eines Dokuments nicht mit HTML-Standards gekoppelt ist können beliebige Tag-Namen vergeben werden.
var meinElement = document.createElement('mein-element');
console.log(meinElement);
Suchen von Elementen
Besonders wichtig bei der Arbeit mit Dokumenten ist die Suche von einzelnen oder mehreren Elementen. Die Schnittstellen für die Suche im Browser hat sich über die letzten Jahre rapide verbessert.
Vorgefertige Kriterien
Die Suche nach einem eindeutigen Element kann mithilfe einer ID ausgeführt werden, insofern das Element eine besitzt.
var inhalt = document.getElementById('inhalt');
console.log(inhalt);
Bei einer Suche wird entweder das Dokument selbst oder aber ein beliebiges Element als Ausgangspunkt verwendet.
var inhalt = document.getElementById('inhalt');
var kommentare = inhalt.getElementById('kommentare');
Werden Elemente einer bestimmten Art gesucht so kann der Tag-Name oder aber die HTML-Klasse als Kriterium genutzt werden.
var elemente = document.getElementsByTagName('body');
console.log(elemente);
var kommentare = document.getElementsByClassName('kommentar');
console.log(kommentare.length);
Bei dieser Art von Suche wird eine NodeList
als Ergebnis zurückgeliefert, ein Array-artiges Objekt von Node-Elementen.
Einzelne Elemente dieser Liste können entweder über eine Funktion oder aber über einen Index abgefragt werden.
var absaetze = document.getElementsByTagName('p');
console.log(absaetze);
console.log(absaetze.item(0));
console.log(absaetze.item(0) == absaetze[0]);
Verweise
Jedes Element-Objekt verweist selbst auf seine übergeordneten und untergeordneten Knoten sowie seine Nachbaren.
var body = document.getElementsByTagName('body').item(0);
for (var i = 0; i < body.childNodes.length; i++) {
console.log(body.childNodes[i]);
}
console.log(body.parentNode);
Diese Referenzen können genutzt werden um sich durch das Dokument zu bewegen und bestimmte Elemente zu finden.
var container = [];
var absaetze = document.getElementsByTagName('p');
for (var i = 0; i < absaetze.length; i++) {
container.push(absaetze[i].parentNode);
}
console.log(container);
CSS-Selektoren
Die Suche mithilfe von vorgefertigen Kriterien und Verweisen auf benachbarte Elemente hat gewisse Limitierungen. Aufgrund dessen enstand durch bekannte Bibliotheken wie jQuery das Konzept von Query-Selektoren, welche heute als CSS-Selektoren standardisiert werden und in vielen Browser verfügbar sind.
Ein CSS-Selektorgruppe beschreibt eine beliebige Menge an Kriterien, die auf ein oder mehrere HTML-Elemente zutrifft. Der Name wurde so gewählt da CSS-Selektoren ebenso in Stylesheets genutzt werden um eine Menge an Elementen auszuwählen. Aufgrund der Kombinierbarkeit einzelner CSS-Selektoren ergibt sich eine sehr mächtige Suchfunktionalität.
Im Browser gibt zwei verschiedene Funktionen um Elemente mithilfe von CSS-Selektoren zu suchen:
document.querySelector
liefert das erste Element zurück welches die Kriterien erfülltdocument.querySelectorAll
liefert alle Elemente zurück welche die Kriterien erfüllen
Mögliche Kriterien
Das einfachste Kriterium für einen CSS-Selektor ist die Angabe eines Tag-Namen.
var absaetze = document.querySelectorAll('p');
console.log(absaetze);
Die Angabe einer ID eines HTML-Elements erfolgt durch eine vorangestellte Raute.
var inhalt = document.querySelector('#inhalt');
console.log(inhalt);
Klassen-Selektoren werden durch einen vorangestellten Punkt gekennzeichnet.
var kommentare = document.querySelectorAll('.kommentar');
console.log(kommentare);
Mithilfe von eckigen Klammern werden beliebige Attribute als Kriterium definiert.
var textFelder = document.querySelectorAll('[type="text"]');
console.log(textFelder);
Wird kein Attributwert vergeben, wird lediglich auf die Existenz des Attributs geprüft.
var versteckteElemente = document.querySelectorAll('[hidden]');
console.log(versteckteElemente);
Die oben genannten Kriterien können beliebig miteinander kombiniert werden.
var wichtigeKommentare = document.querySelectorAll('.kommentar.wichtig');
console.log(wichtigeKommentare);
var versteckteAbsaetze = document.querySelectorAll('p[hidden]');
console.log(textFelder);
Es ist möglich Kriterien für übergeordnete Elemente zu definieren indem man die Selektoren durch ein Leerzeichen trennt.
var versteckteElementeInnerhalbVonKommentaren = document.querySelectorAll('.kommentar [hidden]');
Anmerkung: Die Komplexität einer CSS-Selektorgruppe beeinflusst die Performance einer Suche.
Hinzufügen von Elementen
Neu erstellte Elemente können an beliebigen Stellen im Dokument als untergeordneter Knoten eingefügt werden.
var absatz = document.createElement('p');
document.documentElement.appendChild(p);
Demzufolge kann erst nach einem Element gesucht werden um ihm dann ein neues Element hinzuzufügen.
var body = document.querySelector('body');
var absatz = document.createElement('p');
body.appendChild(absatz);
Modifizieren von Elementen
Mithilfe der entsprechenden DOM-Schnittstellen können Text, Attribute sowie das Styling von Elementen beeinflusst werden.
Ändern von Text
Eine simple aber auch mächtige Weise um den Inhalt eines Elements zu beeinflussen stellt die Eigenschaft innerHTML
dar.
var body = document.querySelector('body');
body.innerHTML = 'Hallo Welt';
Neben einfachen Text-Änderungen lässt sich mithilfe dieser Eigenschaft das gesamte enthaltene HTML beeinflussen.
Anstatt also mithilfe der DOM-Schnittstelle einzelne Elemente zu erzeugen kann einfach Markup als Wert zugewiesen werden.
var body = document.querySelector('body');
body.innerHTML = '<p>Hallo <strong>Welt!</strong></p>';
Ändern von Attributen
Für den Zugriff auf Attribute eines HTML-Elements gibt es die Funktionen getAttribute()
und setAttribute()
.
var p = document.createElement('p');
p.innerHTML = 'Tolle Website!';
p.setAttribute('class', 'kommentar');
document.querySelector('body').appendChild(p);
console.log(p.getAttribute('class'));