Star Wars API – Schritt für Schritt Tutorial
Ein Lernprojekt: Lerne, wie man mit APIs arbeitet, Promises verwendet und das DOM manipuliert.
Was Sie lernen werden
Abschnitt betitelt „Was Sie lernen werden“- HTTP-Requests mit
fetch()senden - JSON-Daten verarbeiten
- Mit Promises arbeiten
- DOM-Elemente dynamisch erstellen und ändern
- Event-Handling in JavaScript
Schritt 1: Projektstruktur anlegen
Abschnitt betitelt „Schritt 1: Projektstruktur anlegen“Erstellen Sie drei Dateien in einem neuen Ordner:
index.html– Die HTML-Strukturstyles.css– Das Stylingscript.js– Die JavaScript-Logik
Schritt 2: HTML-Grundgerüst erstellen
Abschnitt betitelt „Schritt 2: HTML-Grundgerüst erstellen“Sie brauchen eine einfache Struktur mit:
- einem Container-Element (z.B.
<div id="app">) - einem Titel (
<h1>) - einem Suchfeld (
<input>) - einem Such-Button
- einem Button zum Laden aller Charaktere
- einem Bereich für Ergebnisse
- optional Bereichen für Ladeanzeige und Fehlermeldungen
Sinnvolle id-Attribute könnten sein:
- Suchfeld:
searchInput - Such-Button:
searchBtn - “Alle laden”-Button:
loadAllBtn - Ergebnisse-Bereich:
results - Ladeanzeige:
loading - Fehlerbereich:
error
Beispiel-HTML (als Orientierung):
<!DOCTYPE html><html lang="de"><head> <meta charset="UTF-8" /> <title>Star Wars Charaktere</title> <link rel="stylesheet" href="styles.css" /></head><body> <div id="app"> <h1>Star Wars Charaktere</h1>
<div class="controls"> <input id="searchInput" type="text" placeholder="Charakter suchen (z.B. Luke)" /> <button id="searchBtn">Suchen</button> <button id="loadAllBtn">Alle Charaktere laden</button> </div>
<div id="loading" class="hidden">Lade Daten...</div> <div id="error" class="hidden"></div>
<div id="results"></div> </div>
<script src="script.js"></script></body></html>Schritt 3: CSS – Grundstyling
Abschnitt betitelt „Schritt 3: CSS – Grundstyling“Kopieren Sie folgenden CSS-Code in Ihre styles.css-Datei:
* { margin: 0; padding: 0; box-sizing: border-box;}
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background-color: #f5f5f5; padding: 20px;}
#app { max-width: 800px; margin: 40px auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);}
h1 { margin-bottom: 20px; color: #333;}
.controls { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap;}
#searchInput { flex: 1; min-width: 200px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px;}
button { padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background-color 0.2s ease;}
button:hover { background-color: #0056b3;}
#loading { padding: 15px; text-align: center; color: #666; font-style: italic;}
#error { padding: 15px; background-color: #f8d7da; color: #721c24; border-radius: 4px; margin-bottom: 20px;}
.hidden { display: none;}
#results { margin-top: 20px;}Schritt 4: JavaScript – DOM-Elemente referenzieren
Abschnitt betitelt „Schritt 4: JavaScript – DOM-Elemente referenzieren“Mit document.getElementById() holen Sie sich Elemente aus dem DOM.
Beispiel:
// DOM Elementeconst searchInput = document.getElementById('searchInput');const searchBtn = document.getElementById('searchBtn');const loadAllBtn = document.getElementById('loadAllBtn');const results = document.getElementById('results');const loading = document.getElementById('loading');const errorBox = document.getElementById('error');Wir verwenden const, weil sich die Referenz auf das DOM-Element nicht ändern soll.
Der Inhalt des Elements kann sich ändern, aber die Variable zeigt immer auf dasselbe Element.
Schritt 5: Die Star Wars API verstehen
Abschnitt betitelt „Schritt 5: Die Star Wars API verstehen“Basis-URL:
https://swapi.py4e.com/apiWichtige Endpunkte:
https://swapi.py4e.com/api/people/– Liste aller Charakterehttps://swapi.py4e.com/api/people/?search=Luke– Suche nach “Luke”
Die Antwortstruktur sieht so aus:
{ "count": 82, "next": "https://swapi.py4e.com/api/people/?page=2", "previous": null, "results": [ { "name": "Luke Skywalker", "height": "172", "mass": "77", "hair_color": "blond", "skin_color": "fair", "eye_color": "blue", "birth_year": "19BBY", "gender": "male" } ]}Schritt 6: fetch() verstehen
Abschnitt betitelt „Schritt 6: fetch() verstehen“Grundstruktur:
fetch(url) .then(response => response.json()) .then(data => { // Daten verwenden }) .catch(error => { // Fehler behandeln });fetch() liefert ein Promise zurück.
Wir erstellen eine Hilfsfunktion fetchData(url):
const API_URL = 'https://swapi.py4e.com/api';
function fetchData(url) { return fetch(url) .then(response => { if (!response.ok) { throw new Error('HTTP-Fehler: ' + response.status); } return response.json(); }) .catch(err => { console.error('Fetch-Fehler:', err); throw err; });}Schritt 7: Ladeanzeige und Fehlerbehandlung
Abschnitt betitelt „Schritt 7: Ladeanzeige und Fehlerbehandlung“Verwenden Sie Klassen wie .hidden, um Elemente ein- und auszublenden.
Beispiel:
function showLoading() { errorBox.classList.add('hidden'); loading.classList.remove('hidden');}
function hideLoading() { loading.classList.add('hidden');}
function showError(message) { errorBox.textContent = message; errorBox.classList.remove('hidden');}Es ist sinnvoll, die Ladeanzeige zu zeigen, bevor ein Request startet,
damit der Nutzer weiß, dass gerade Daten geladen werden.
Schritt 8: Alle Charaktere laden
Abschnitt betitelt „Schritt 8: Alle Charaktere laden“Funktion loadAllCharacters():
function loadAllCharacters() { showLoading(); results.innerHTML = ''; errorBox.classList.add('hidden');
const url = `${API_URL}/people/`;
fetchData(url) .then(data => { hideLoading(); displayCharacters(data.results); // Optional: Pagination (Schritt 13) if (data.next) { loadNextPage(data.next); } }) .catch(err => { hideLoading(); showError('Fehler beim Laden der Charaktere.'); console.error(err); });}Schritt 9: Charaktere in der Liste anzeigen
Abschnitt betitelt „Schritt 9: Charaktere in der Liste anzeigen“Funktion displayCharacters(characters):
function displayCharacters(characters) { characters.forEach(character => { const div = document.createElement('div'); div.className = 'item';
div.innerHTML = ` <h3>${character.name}</h3> <p>Geschlecht: ${character.gender}, Geburtsjahr: ${character.birth_year}</p> `;
div.addEventListener('click', () => { showDetails(character); });
results.appendChild(div); });}Ein Charakter-Objekt hat z.B. Eigenschaften:
namegenderbirth_yearheightmasseye_colorhair_color- usw.
Schritt 10: Event Listener hinzufügen
Abschnitt betitelt „Schritt 10: Event Listener hinzufügen“Verbinde Buttons mit Funktionen:
loadAllBtn.addEventListener('click', () => { loadAllCharacters();});
searchBtn.addEventListener('click', () => { const term = searchInput.value.trim(); if (term) { searchCharacters(term); } else { showError('Bitte einen Suchbegriff eingeben.'); }});
// Bonus: Enter-Taste im SuchfeldsearchInput.addEventListener('keydown', event => { if (event.key === 'Enter') { const term = searchInput.value.trim(); if (term) { searchCharacters(term); } else { showError('Bitte einen Suchbegriff eingeben.'); } }});Schritt 11: Suchfunktion implementieren
Abschnitt betitelt „Schritt 11: Suchfunktion implementieren“Funktion searchCharacters(searchTerm):
function searchCharacters(searchTerm) { showLoading(); results.innerHTML = ''; errorBox.classList.add('hidden');
const url = `${API_URL}/people/?search=${encodeURIComponent(searchTerm)}`;
fetchData(url) .then(data => { hideLoading(); if (data.results.length === 0) { showError('Keine Charaktere gefunden.'); } else { displayCharacters(data.results); } }) .catch(err => { hideLoading(); showError('Fehler bei der Suche.'); console.error(err); });}encodeURIComponent() ist wichtig, damit Sonderzeichen (z.B. Leerzeichen) korrekt in der URL kodiert werden.
Schritt 12: Details anzeigen
Abschnitt betitelt „Schritt 12: Details anzeigen“Beim Klick auf einen Charakter sollen Details erscheinen.
In displayCharacters() haben wir bereits:
div.addEventListener('click', () => { showDetails(character);});Implementieren Sie showDetails(character):
function showDetails(character) { const html = ` <div class="details"> <h2>${character.name}</h2> <div class="detail-row"> <strong>Geburtsjahr:</strong> ${character.birth_year} </div> <div class="detail-row"> <strong>Geschlecht:</strong> ${character.gender} </div> <div class="detail-row"> <strong>Größe:</strong> ${character.height} cm </div> <div class="detail-row"> <strong>Gewicht:</strong> ${character.mass} kg </div> <div class="detail-row"> <strong>Augenfarbe:</strong> ${character.eye_color} </div> <button id="backBtn">Zurück zur Liste</button> </div> `; results.innerHTML = html;
const backBtn = document.getElementById('backBtn'); backBtn.addEventListener('click', () => { loadAllCharacters(); });}Schritt 13: Pagination (optional, empfohlen)
Abschnitt betitelt „Schritt 13: Pagination (optional, empfohlen)“Die API liefert eine Seite von Ergebnissen und Links zu weiteren Seiten via next.
Rekursive Funktion loadNextPage(url):
function loadNextPage(url) { fetchData(url) .then(data => { displayCharacters(data.results); if (data.next) { loadNextPage(data.next); } }) .catch(err => { console.error('Fehler beim Laden weiterer Seiten:', err); });}In loadAllCharacters() können Sie nach dem ersten Request, wenn data.next existiert, loadNextPage(data.next) aufrufen.
Die Funktion ist rekursiv, weil sie sich selbst aufruft, solange noch eine next-URL vorhanden ist.
Schritt 14: CSS verfeinern
Abschnitt betitelt „Schritt 14: CSS verfeinern“Ergänzen Sie Ihre styles.css-Datei um folgende Styles für die Charakter-Liste und Detailansicht:
.item { border: 1px solid #ddd; border-radius: 6px; padding: 12px; margin-bottom: 8px; background: #f9f9f9; transition: background-color 0.2s ease; cursor: pointer;}
.item:hover { background-color: #eaeaea;}
.details { border: 1px solid #ccc; border-radius: 8px; padding: 20px; background: #fff; margin-top: 20px;}
.details h2 { margin-bottom: 15px; color: #333;}
.detail-row { margin-bottom: 10px; padding: 8px 0; border-bottom: 1px solid #eee;}
.detail-row:last-child { border-bottom: none;}
.detail-row strong { display: inline-block; min-width: 120px; color: #555;}
#backBtn { margin-top: 20px; background-color: #6c757d;}
#backBtn:hover { background-color: #5a6268;}Schritt 15: Feinschliff und Testing
Abschnitt betitelt „Schritt 15: Feinschliff und Testing“Testen:
- Laden aller Charaktere
- Suche nach verschiedenen Begriffen
- Anzeige der Detailansicht
- Verhalten bei leerer Suche
- Verhalten ohne Internet (oder mit falscher URL)
Mögliche Verbesserungen:
- Suchfeld nach erfolgreicher Suche leeren
- Loading-Spinner statt Text
- Responsives Design für mobile Geräte
- Bessere, genauere Fehlermeldungen
Verständnisfragen
Abschnitt betitelt „Verständnisfragen“- Promises:
Warum verwenden wir.then()und.catch()? Was passiert, wenn wir diese nicht verwenden? - DOM-Manipulation:
Was ist der Unterschied zwischeninnerHTMLundtextContent? Wann würden Sie welches verwenden? - Event Handling:
Was passiert, wenn SieaddEventListenermehrmals auf demselben Element aufrufen? - API-Requests:
Warum müssen wirencodeURIComponent()für Suchbegriffe verwenden? - Asynchronität:
Warum zeigt die Ladeanzeige manchmal nicht sofort? (Hinweis: JavaScript ist single-threaded.)
Erweiterungsmöglichkeiten
Abschnitt betitelt „Erweiterungsmöglichkeiten“Wenn Sie das Projekt verstanden haben, können Sie es ausbauen:
- Favoriten: Speichern Sie ausgewählte Charaktere im
localStorage. - Vergleich: Zeigen Sie zwei Charaktere nebeneinander zum Vergleichen.
- Filtern: Filter nach Geschlecht oder Geburtsjahr.
- Weitere Ressourcen: Filme, Planeten, Raumschiffe aus der SWAPI einbinden.
- Debouncing: Suche erst auslösen, wenn der Nutzer kurz aufgehört hat zu tippen.
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“Sie haben gelernt:
- HTTP-Requests mit
fetch()zu senden - JSON-Daten zu verarbeiten
- Mit Promises zu arbeiten
- DOM-Elemente dynamisch zu erzeugen und zu verändern
- Event-Handler sinnvoll zu nutzen
- Eine kleine Web-App mit einer öffentlichen API zu erstellen
Nächste Schritte:
- Lies die MDN-Dokumentation zu
fetch() - Probiere andere öffentliche APIs aus
- Lerne
async/awaitals Alternative zu.then()und.catch()
Viel Erfolg beim weiteren Experimentieren mit JavaScript und APIs!