Javascript XHR – XMLHttpRequest

XMLHttprequest Grundlagen: XML-Datei vom Server lesen

Der XMLHttpRequest (kurz XHR) tauscht Daten zwischen Client und Server im Hintergrund aus, ohne die Seite komplett neu zu laden. Diese Technik ist als Ajax (Asynchronous Javascript And XML) bekannt geworden: Das war der Kern von Web 2.0.

Daten im Hintergrund laden statt Seite neu laden

Javascript XHR prüft z.B. Formulareingaben gegen die Datenbank ohne Hin- und Her zwischen Client und der PHP-Anwendungen auf dem Server, und lädt zusätzliche Informationen. Bei der klassischen Webanwendung (z.B. bei einer Anfrage an ein PHP-Skript) lösen Aktionen des Benutzers einen HTTP-Request zurück zum Server aus.

Kein XHR

Das PHP-Script verifiziert den Benutzer, liest Daten aus der Datenbank, berechnet ein paar Zahlen und lieferte dann eine neue HTML-Seite an den Client aus. Dabei bestand die neue Seite zum größten Teil aus denselben Elementen wie vorher und nur wenige Inhalte wurden tatsächlich geändert.

XMLHttpRequest: Datei vom Server laden

XHR ist eine Schnittstelle zwischen Javascript und den Daten auf dem Server. Im einfachsten Fall holt Javascript Daten aus einer XML- oder JSON-Datei vom Server – ohne Beteiligung einer serverseitigen Anwendung.

Ablauf des XMLHttpRequest beim Laden einer XML-Datei
Mit dem XMLHttpRequest fordert Javascript z.B. eine Datei auf dem Server an und bindet die Daten in das Dokument ein, ohne die Seite neu zu laden.

Der Name XMLHttpRequest ist etwas irreführend, denn der XMLHttpRequest unterstützt nicht nur XML, sondern alle textbasierten Formate, und sowohl Requests als Antworten (Response).

Alles, was Javascript braucht, um eine JSON-Datei vom Server zu laden, ist ein HTML-Element für die Daten.

<ul id="products"></ul>

Die JSON-Datei enthält ein Array von Objekten, die wiederum die Informen zu drei Bildern enthalten.

[
	{"src": "lampe-01.webp", "width": 531, "height": 509, "alt": "Lampe »Munkola«"},
	{"src": "lampe-02.webp", "width": 531, "height": 509, "alt": "Lampe »Mirmis«"},
	{"src": "lampe-03.webp", "width": 531, "height": 509, "alt": "Lampe »Jolii«"}
]

Als erste Aktion initialisiert das Skript einen XMLHttpRequest und ruft open () auf. open () braucht zwei Argumente: die Art des Requests und die URL der JSON-Datei auf dem Server.

const xhr = new XMLHttpRequest ();
xhr.open ("GET", "products.json");

xhr.onreadystatechange = function () {
	if (xhr.readyState === 4 && xhr.status === 200) {
		console.log (this.readyState);
	}
}

xhr.send ();

onreadystatechange wartet auf das Eintreffen eines readystatechange-Events. readystatechange kommt mit einem von fünf möglichen Werten:

  1. Request wurde nicht initialisiert
  2. Verbindung zum Server ist aufgebaut
  3. Request erhalten
  4. Request wird ausgeführt
  5. Request beendet und die Antwort steht bereit

Am Ende sendet xhr.send () den Request. Schön zu sehen: Die Rückgabewerte geben den Ablauf des Requests wieder.


2
3
4

Wir interessieren uns nur für den Wert 4 und den Status-Code 200 (Request erfolgreich). this.readyState und this.status sind auch die Werte, die Hinweise geben, wenn der Request nicht ausgeführt wird.

Um die Daten in die Webseite zu setzen, holt forEach () die Informationen zu jedem der drei Objekte innerhalb der if-Abfrage.

if (this.readyState === 4 && this.status === 200) {
   const data = JSON.parse (this.responseText);
   data.forEach (item => {
      const li = document.createElement ("li");
      const img = document.createElement ("img");
      img.src = item.src;
      li.append (img);
      document.querySelector ("#producs").append (li);
   });
}

    XMLHttpRequest für ein API

    Im Grunde genommen funktioniert das Einholen von Informationen aus einer Anwendung auf einer anderen Webseite über ein API (Application Programming Interface) kaum anders.

    Viele Anwendungen stellen Programmierschnittstellen zur Verfügung, an denen sie Daten für andere Webseiten oder Anwendungen anbieten. Das Spektrum reicht von Wetterdaten über Straßenkarten (z.B. Openstreetmap auf https://wiki.openstreetmap.org/wiki/API) und den APIs der Social Media bis hin zu Github-Benutzern und -Projekten. Generell bestehen APIs aus Befehlen, Funktionen, Protokollen und Objekten. Jedoch unterscheidet sich die Form von Anbieter zu Anbieter: Ein API ist kein Heimspiel wie die JSON-Datei auf dem eigenen Webspace.

    openthesaurus.de ist ein Projekt im Internet, mit dem Autoren nach Synonymen und Assoziationen für einen Begriff suchen können. Das API von openthesaurus.de bietet wahlweise XML- oder JSON als Austauschformat.

      <input id="thesaurus" value="Türöffner" type="text" placeholder="Gib mir ein Wort!"/>
      <button id="btn">Suche</button>
      <ul id="alt"></ul>
      

      Die Variable word enthält den Begriff, für den ein Ersatz gesucht wird.

      const button = document.querySelector ("#btn");
      const xhr = new XMLHttpRequest ();
      
      button.addEventListener ("click", function () {
      	const xhr = new XMLHttpRequest ();
      	const thesaurus = document.querySelector ("#thesaurus");
      	const word = encodeURI(thesaurus.value);
      	const url = `https://www.openthesaurus.de/synonyme/search?q=${word}&format=application/json`;
      	xhr.open ("GET", url);
      	xhr.onreadystatechange = function () {
      		if (this.readyState === 4 && this.status === 200) {
      			const data = JSON.parse (this.responseText);
      			console.log (data);
      			resolve (data);
      		}
      	}
      	xhr.send ();
      });
      

      Die Ausgabe der JSON-Daten in der Konsole des Browsers zeigt die Struktur der gewünschten Informationen. Die Struktur der Daten eines APIs ist immer wieder eine andere und muss aufgeschlüsselt werden.

      
      ▼ synsets: Array (2)
        ▶ 0 {id: 37879, categories: [], terms: [{term: "ein Anfang (sein)"}, … ]}
        ▶ 1 {id: 43686, categories: [], terms: [{term: "Türen öffnen"}, … ]}
      
      

      Die Funktion resolve löst die gelieferten Objekte auf und überträgt sie in die Liste.

      function resolve (data) {
      	data.synsets.forEach (item => {
      		item.terms.forEach (
      			e => {
      				const li = document.createElement ("li");
      				li.innerHTML = e.term;
      				document.querySelector ("#alt").append (li)
      			}
      		)
      	});
      }
      

      fetch oder XMLHttpRequest?

      Wenn keine Rücksicht auf völlig veraltete Browser nötig ist, wird heute eher fetch als der XMLHttp-Request verwendet. fetch ist einfacher zu nutzen, flexibler und die Promise-basierte Syntax macht den Code besser lesbar.

      fetch ("booklist.json")
      	.then (response => response.json ())
      	.then (data => console.log (data))
      	.catch (error => console.error ("Fehler:", error));
      

      Prüfen, ob ein Bild oder ein Link existiert

      
      
      (function(){
      	let xhr = new XMLHttpRequest();
          xhr.open('HEAD', "xmlhttp.png", true);
          xhr.send();
           
          if (xhr.status == "404") {
          	console.log ("Gibts nicht");
              return false;
          } else {
          	console.log ("Alles OK xmlhttp.png gibt es");
              return true;
          }
      })();
      

      IE11 Unterstützung XHR ? XHR open() und send()

      Wenn IE11 noch unter den Hut gebracht werden muss, geht die Zeitreise zurück in die Vergangenheit. Die gebräuchlichste Anweisungssequenz für einen XMLHttpRequest bestand früher aus

      1. dem Erzeugen einer Instanz des XMLHttpRequest-Objekts let xhr = new XMLHttpRequest(),
      2. Aufruf der open()-Methode,
      3. Abfragen von onreadystatechange und Aufruf einer Funktion, die beim Eintreffen der Antwort die Verarbeitung übernimmt.
      4. Senden des Requests mit der send()-Methode
      let xhr = new XMLHttpRequest();
      
      //         Methode  Datei       asynchron
      xhr.open ( "GET",  "books.xml", true );
      
      xhr.onreadystatechange = function () {
          if (xhr.readyState == 4 && xhr.status == 200) {
              //console.log (xhr.responseText);
              document.querySelector(".listxmp").innerHTML = xhr.responseText;
          }
      }
      
      xhr.send ();
      

      xmlObj.open sendet den GET-Request (nur Daten holen) an den Server. Für die Verarbeitung von Formulardaten wird POST benutzt. Dann wäre der erste Parameter von xhr.open ein POST, und der Parameter von xhr.send wären die Daten, die an den Server gesendet würden.

      Der dritte Parameter steht für async = true. Der Request wird asynchron (quasi gleichzeitig) ausgeführt und blockiert die Seite nicht.

      Wann liegt die Antwort auf den Request vor? xhr.onreadystatechange muss vor dem Senden des Requests mit xhr.send() aufgerufen werden.

      onreadystatechange gibt den Ablauf der Abfrage zurück und durchläuft die Werte 0 bis 4. onreadystatechange feuert bis zur Antwort "4" ununterbrochen den Status der Abfrage. Heute können wir anstelle von xhr.onreadystatechange xhr.onload nutzen. onload meldet sich nur, wenn der Request erfolgreich durchgeführt wurde.

      xhr.responseText ist ein XMLHttpRequest-Objekt, dessen Methoden alle Operationen steuern und dessen Eigenschaften die Daten speichern, die der Server zurückgeliefert.

      readystatechange ist das Event, dass alle Antworten abfängt. Ursprünglich war readystatechange das einzige Ereignis des XMLHttpRequest, aber mit XMLHttpRequest Version 2 sind weitere Events wie XHR onload und onerror hinzugekommen.