Erstellen einer einfachen Wetter-App mit Vanilla JavaScript
() translation by (you can also view the original English article)



Im heutigen Tutorial erfahren Sie, wie Sie mit Vanilla JavaScript eine einfache, aber voll funktionsfähige Wetter-App erstellen. Wir haben viele interessante Dinge zu besprechen, also nimm eine Tasse Kaffee und lass uns anfangen!
Was werden wir erstellen?
Hier ist ein Einführungsvideo, das die Funktionalität der App zeigt, die wir erstellen werden:
Hier ist die Demo auf CodePen, mit der Sie spielen können:
Hinweis: In diesem Tutorial wird davon ausgegangen, dass Sie mit AJAX, einer wichtigen Front-End-Technik, vertraut sind. Wenn Sie gerade erst anfangen, schauen Sie sich diese Serie an.
1. Gerüst der App
Bevor wir mit der Erstellung unserer App beginnen, müssen wir einige Dinge berücksichtigen.
Suchen Sie eine Wetter-API
Zuerst müssen wir einen Anbieter finden, der es uns ermöglicht, seine Wetterdaten in unsere App zu integrieren. Glücklicherweise gibt es verschiedene Anbieter für die Entwicklung von Wetter-Apps. Die meisten von ihnen enthalten ein kostenloses Paket sowie Premium-Abonnements, die je nach Service / Funktionen skaliert werden können.
In unserem Fall verwenden wir OpenWeatherMap, eine der beliebtesten kostenlosen Optionen. Um seine Funktionen nutzen zu können, müssen wir uns zunächst für einen API-Schlüssel anmelden:



Dieser Service wird mit verschiedenen Paketen geliefert. Wie Sie der folgenden Visualisierung entnehmen können, ermöglicht der Starter (kostenlos) 60 Anrufe pro Minute, was unseren Anforderungen entspricht:



Bevor Sie fortfahren, stellen Sie bitte sicher, dass Sie sich für einen API-Schlüssel registriert haben. Später werden wir diesen Schlüssel in unser Skript aufnehmen.
Denken Sie daran, dass Sie die App am besten testen können, indem Sie die Codepen-Demo abspalten und Ihren eigenen Schlüssel hinzufügen. Wenn wir alle denselben Schlüssel verwenden, funktioniert die App aufgrund von API-Aufrufbeschränkungen wahrscheinlich nicht.
Finden der Wettersymbole
Für die Zwecke dieses Tutorials benötigen wir eine Reihe von Wettersymbolen. Es ist erwähnenswert, dass OpenWeatherMap über ein eigenes Symbol-Set verfügt, das wir uns ansehen werden. Wir werden jedoch noch einen Schritt weiter gehen und einige benutzerdefinierte verwenden.
Wir werden erneut die Envato Elements-Bibliothek nutzen und ein Paket mit Vektorwettersymbolen herunterladen:



2. Definieren Sie das Seiten-Markup
Wir werden zwei Abschnitte definieren.
Der erste Abschnitt enthält eine Überschrift, ein Suchformular und ein leeres span
-Element. Dieses Element wird unter bestimmten Bedingungen mit einer entsprechenden Meldung sichtbar. Insbesondere, wenn für eine angeforderte Stadt keine Wetterdaten verfügbar sind oder die Daten für diese Stadt bereits bekannt sind.
Der zweite Abschnitt enthält eine Liste der Städte. Standardmäßig enthält es keine Städte. Wenn wir jedoch nach dem Wetter für eine bestimmte Stadt suchen und Wetterdaten verfügbar sind, wird ein entsprechendes Listenelement (Stadt) an die ungeordnete Liste angehängt.
Hier ist das erste Seiten-Markup:
1 |
<section class="top-banner"> <div class="container"> <h1 class="heading">Simple Weather App</h1> <form> <input type="text" placeholder="Search for a city" autofocus> <button type="submit">SUBMIT</button> <span class="msg"></span> </form> </div> </section> <section class="ajax-section"> <div class="container"> <ul class="cities"></ul> </div> </section> |
2 |
Hinweis: In unserer CodePen-Demo funktioniert das autofokus
-Attribut des Suchfelds nicht. Tatsächlich wird der folgende Fehler ausgegeben, den Sie sehen können, wenn Sie Ihre Browserkonsole öffnen:



Wenn Sie diese App jedoch lokal ausführen (nicht als Codepen-Projekt), tritt dieses Problem nicht auf.
Und hier ist das Markup für ein Listenelement, das wir dynamisch über JavaScript generieren:
1 |
<li class="city"> <h2 class="city-name" data-name="..."> <span>...</span> <sup>...</sup> </h2> <span class="city-temp">...<sup>°C</sup></span> <figure> <img class="city-icon" src="..." alt="..."> <figcaption>...</figcaption> </figure> </li> |
2 |
2. Geben Sie einige grundlegende Stile an
Nachdem das Markup für die App fertig ist, werden wir mit dem CSS fortfahren. Der erste Schritt besteht wie immer darin, einige CSS-Variablen und allgemeine Rücksetzstile anzugeben:
1 |
:root { --bg_main: #0a1f44; --text_light: #fff; --text_med: #53627c; --text_dark: #1e2432; --red: #ff1e42; --darkred: #c3112d; --orange: #ff8c00; } * { margin: 0; padding: 0; box-sizing: border-box; font-weight: normal; } button { cursor: pointer; } input { -webkit-appearance: none; } button, input { border: none; background: none; outline: none; color: inherit; } img { display: block; max-width: 100%; height: auto; } ul { list-style: none; } body { font: 1rem/1.3 "Roboto", sans-serif; background: var(--bg_main); color: var(--text_dark); padding: 50px; } |
2 |
4. Legen Sie die Hauptstile fest
Lassen Sie uns nun die Hauptstile unserer App diskutieren.
Abschnitt #1 Stile
Zunächst fügen wir den Elementen des ersten Abschnitts einige einfache Stile hinzu.
Auf mittleren Bildschirmen und höher (>700px) sollte das Layout folgendermaßen aussehen:



Auf kleineren Bildschirmen werden die Formularelemente in zwei Zeilen aufgeteilt:



Hier sind die zugehörigen Stile:
1 |
/*CUSTOM VARIABLES HERE*/ .top-banner { color: var(--text_light); } .heading { font-weight: bold; font-size: 4rem; letter-spacing: 0.02em; padding: 0 0 30px 0; } .top-banner form { position: relative; display: flex; align-items: center; } .top-banner form input { font-size: 2rem; height: 40px; padding: 5px 5px 10px; border-bottom: 1px solid; } .top-banner form input::placeholder { color: currentColor; } .top-banner form button { font-size: 1rem; font-weight: bold; letter-spacing: 0.1em; padding: 15px 20px; margin-left: 15px; border-radius: 5px; background: var(--red); transition: background 0.3s ease-in-out; } .top-banner form button:hover { background: var(--darkred); } .top-banner form .msg { position: absolute; bottom: -40px; left: 0; max-width: 450px; min-height: 40px; } @media screen and (max-width: 700px) { .top-banner form { flex-direction: column; } .top-banner form input, .top-banner form button { width: 100%; } .top-banner form button { margin: 20px 0 0 0; } .top-banner form .msg { position: static; max-width: none; min-height: 0; margin-top: 10px; } } |
2 |
Abschnitt #2 Stile
Wir werden CSS Grid verwenden, um die Listenelemente zu gestalten. Denken Sie daran, dass jedes Listenelement eine Stadt darstellt. Ihre Breite hängt von der Bildschirmgröße ab.
Auf großen Bildschirmen ( >1000px) haben wir ein vierspaltiges Layout.



Dann werden auf mittleren Bildschirmen ( > 700px und ≤1000px) ein dreispaltiges Layout, auf kleinen Bildschirmen ( > 500px und ≤700px) ein zweispaltiges Layout und schließlich auf besonders kleinen Bildschirmen(≤500px) alle Elemente gestapelt.
Hier sind die entsprechenden Stile:
1 |
.ajax-section { margin: 50px 0 20px; } .ajax-section .cities { display: grid; grid-gap: 32px 20px; grid-template-columns: repeat(4, 1fr); } @media screen and (max-width: 1000px) { .ajax-section .cities { grid-template-columns: repeat(3, 1fr); } } @media screen and (max-width: 700px) { .ajax-section .cities { grid-template-columns: repeat(2, 1fr); } } @media screen and (max-width: 500px) { .ajax-section .cities { grid-template-columns: repeat(1, 1fr); } } |
2 |
Jede Spalte sieht aus wie eine Karte mit einem unteren Schatten, der über das Pseudoelement ::after
hinzugefügt wird.
Auf der Karte finden Sie Wetterinformationen zur gewünschten Stadt. Diese werden, abgesehen von den Symbolen, von unserer Anfrage stammen. Diese Symbole, die wie oben erwähnt von Envato Elements übernommen wurden, zeigen die aktuellen Wetterbedingungen dieser Stadt an und stimmen mit den entsprechenden OpenWeatherMap-Symbolen überein.

Unten sehen Sie einen Teil des CSS, der für dieses Layout benötigt wird:
1 |
/*CUSTOM VARIABLES HERE*/ .ajax-section .city { position: relative; padding: 40px 10%; border-radius: 20px; background: var(--text_light); color: var(--text_med); } .ajax-section .city::after { content: ’’; width: 90%; height: 50px; position: absolute; bottom: -12px; left: 5%; z-index: -1; opacity: 0.3; border-radius: 20px; background: var(--text_light); } .ajax-section figcaption { margin-top: 10px; text-transform: uppercase; letter-spacing: 0.05em; } .ajax-section .city-temp { font-size: 5rem; font-weight: bold; margin-top: 10px; color: var(--text_dark); } .ajax-section .city sup { font-size: 0.5em; } .ajax-section .city-name sup { padding: 0.2em 0.6em; border-radius: 30px; color: var(--text_light); background: var(--orange); } .ajax-section .city-icon { margin-top: 10px; width: 100px; height: 100px; } |
2 |
5. Fügen Sie das JavaScript hinzu
An diesem Punkt sind wir bereit, die Kernfunktionalität unserer App zu erstellen. Lassen Sie es uns tun!
Beim Einreichen des Formulars
Jedes Mal, wenn ein Benutzer das Formular durch Drücken der Eingabetaste oder der Schaltfläche "Senden" sendet, werden zwei Dinge ausgeführt:
- Verhindern Sie das Senden des Formulars und verhindern Sie daher das erneute Laden der Seite.
- Holen Sie sich den Wert, der im Suchfeld enthalten ist.
Hier ist der Startcode:
1 |
const form = document.querySelector(".top-banner form"); form.addEventListener("submit", e => { e.preventDefault(); const inputVal = input.value; }); |
2 |
Als Nächstes prüfen wir, ob sich im zweiten Abschnitt Listenelemente (Städte) befinden.
Führen Sie eine AJAX-Anforderung durch
Wir gehen davon aus, dass die Liste leer ist. Das heißt, es hat in der Vergangenheit noch nie eine AJAX-Anfrage ausgeführt. In diesem Fall führen wir eine Anforderung an die OpenWeatherMap-API aus und übergeben die folgenden Parameter:
- Der Städtename (z. B. Athen) oder der durch Kommas getrennte Städtename zusammen mit dem Ländercode (z. B. Athen, gr), der der Wert des Suchfelds ist
- Der API-Schlüssel. Auch hier sollten Sie Ihren eigenen Schlüssel verwenden, um unerwartete Fehler aufgrund von API-Aufrufbeschränkungen zu vermeiden.
- Die Temperatureinheit für die gewünschte Stadt. In unserem Fall gehen wir mit Celcius.
Vor diesem Hintergrund sollte unsere Anforderungs-URL gemäß der API-Dokumentation ungefähr so aussehen:
1 |
const apiKey = "YOUR_OWN_KEY"; const inputVal = input.value; ... const url = `https://api.openweathermap.org/data/2.5/weather?q=${inputVal}&appid=${apiKey}&units=metric`; |
2 |
Um die AJAX-Anfrage auszuführen, haben wir viele Optionen. Wir können die einfache alte XMLHttpRequest-API, die neuere Fetch-API oder sogar eine JavaScript-Bibliothek wie jQuery und Axios verwenden. In diesem Beispiel verwenden wir die Fetch-API.
Um die gewünschten Daten zu erhalten, müssen wir folgende Schritte ausführen:
- Übergeben Sie die URL, auf die wir zugreifen möchten, an die Methode
fetch()
. - Diese Methode gibt ein Versprechen zurück, das die Antwort enthält (ein
Response
-Objekt). Dies ist jedoch nicht die eigentliche Antwort, sondern nur eine HTTP-Antwort. Um die Antwortdaten im gewünschten JSON-Format abzurufen (dies ist das Standarddatenformat von OpenWeatherMap), verwenden wir diejson()
-Methode des Antwortobjekts. - Diese Methode gibt ein weiteres Versprechen zurück. Wenn es erfüllt ist, stehen die Daten zur Bearbeitung zur Verfügung.
- Wenn die Anforderung aus irgendeinem Grund nicht erfolgreich ist, wird eine entsprechende Meldung auf dem Bildschirm angezeigt.
Unsere AJAX-Anfrage würde also ungefähr so aussehen:
1 |
... fetch(url) .then(response => response.json()) .then(data => { // do stuff with the data }) .catch(() => { msg.textContent = "Please search for a valid city 😩"; }); |
2 |
Tipp: Anstatt
then()
s zu verketten, hätten wir den neueren und besser lesbaren
async/await
-Ansatz für die AJAX-Anforderung verwenden können..



1 |
const { main, name, sys, weather } = data; const icon = `https://openweathermap.org/img/wn/${ weather[0]["icon"] }@2x.png`; const li = document.createElement("li"); li.classList.add("city"); const markup = ` <h2 class="city-name" data-name="${name},${sys.country}"> <span>${name}</span> <sup>${sys.country}</sup> </h2> <div class="city-temp">${Math.round(main.temp)}<sup>°C</sup> </div> <figure> <img class="city-icon" src=${icon} alt=${weather[0]["main"]}> <figcaption>${weather[0]["description"]}</figcaption> </figure> `; li.innerHTML = markup; list.appendChild(li); |
2 |
Hier müssen wir zwei Dinge besprechen:
- Wenn Sie sich die obige Antwortvisualisierung noch einmal ansehen, werden Sie feststellen, dass die API einen icon-Code (z. B. "50d") zurückgibt, der die aktuellen Wetterbedingungen für die Zielstadt enthält. Basierend auf diesem Code können wir die Symbol-URL erstellen und über das img-Tag auf der Karte anzeigen.
- Innerhalb des .city-name-Elements jedes Listenelements wird das Attribut data-name mit dem Wert cityName,countryCode (z. B. madrid,es) angehängt. Später werden wir diesen Wert verwenden, um doppelte Anforderungen zu vermeiden.
Dinge zurücksetzen
Zuletzt löschen wir nach der AJAX-Anforderung den Inhalt des .msg-Elements und den Wert des Suchfelds und konzentrieren uns auch auf dieses Feld:
1 |
... msg.textContent = ""; form.reset(); input.focus(); |
2 |
Großartige Arbeit, Leute! Wir haben gerade die erste Version unserer App erstellt. Wenn Sie Ihren eigenen API-Schlüssel eingeben und nach einer Stadt suchen, sollte ein Kartenlayout ähnlich dem folgenden angezeigt werden:



Hier ist die zugehörige CodePen-Demo:
Fügen Sie benutzerdefinierte Symbole hinzu
Passen wir jetzt das Erscheinungsbild unserer App ein wenig an. Wir ersetzen die Standard-OpenWeatherMap-PNG-Symbole durch die SVGs, die wir zuvor von Envato Elements heruntergeladen haben.
Zu diesem Zweck habe ich alle neuen Symbole in Codepen hochgeladen (über den Asset Manager, da ich ein PRO-Mitglied bin) und ihre Namen geändert, sodass sie den Namen und Wetterbedingungen der ursprünglichen Symbole wie folgt entsprechen:



Dann müssen wir im Code nur den Symbolpfad ändern:
1 |
//BEFORE const icon = `https://openweathermap.org/img/wn/${ weather[0]["icon"] }@2x.png`; //AFTER const icon = `https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/${ weather[0]["icon"] }.svg`;
|
2 |
Doppelte Anfragen verhindern
Eines müssen wir noch beheben. Bisher wird bei der Durchführung einer erfolgreichen AJAX-Anforderung ein Listenelement erstellt. Die Liste kann jedoch mehrere identische Listenelemente enthalten, die sich auf dieselbe Stadt beziehen, wie z.B.



Das ist eine schlechte Benutzererfahrung. Stellen Sie daher sicher, dass nur eine einzige Anfrage für eine bestimmte Stadt ausgelöst wird.
Aber vorher gibt es noch etwas zu berücksichtigen. Der gleiche Städtename kann in mehr als einem Land existieren. Wenn wir beispielsweise im Sucher von OpenWeatherMap nach "Athen" suchen, werden folgende Ergebnisse angezeigt:



Vor diesem Hintergrund schreiben wir einen Code, der sicherstellt, dass nur eine einzige Anfrage pro Stadt und Land ausgeführt wird:
1 |
... //1 const listItems = list.querySelectorAll(".ajax-section .city"); const listItemsArray = Array.from(listItems); if (listItemsArray.length > 0) { //2 const filteredArray = listItemsArray.filter(el => { let content = ""; //athens,gr if (inputVal.includes(",")) { //athens,grrrrrr->invalid country code, so we keep only the first part of inputVal if (inputVal.split(",")[1].length > 2) { inputVal = inputVal.split(",")[0]; content = el.querySelector(".city-name span").textContent.toLowerCase(); } else { content = el.querySelector(".city-name").dataset.name.toLowerCase(); } } else { //athens content = el.querySelector(".city-name span").textContent.toLowerCase(); } return content == inputVal.toLowerCase(); }); //3 if (filteredArray.length > 0) { msg.textContent = `You already know the weather for ${ filteredArray[0].querySelector(".city-name span").textContent } ...otherwise be more specific by providing the country code as well 😉`; form.reset(); input.focus(); return; } } |
2 |
Lassen Sie mich erklären, welche Aktionen hier stattfinden:
- Während des Submit-Handlers prüfen wir erneut, bevor eine AJAX-Anfrage gestellt wird, ob die ungeordnete Liste leer ist oder nicht. Wenn es nicht leer ist, bedeutet dies, dass mindestens eine erfolgreiche AJAX-Anforderung bereits ausgeführt wurde.
- Als Nächstes prüfen wir, ob ein Listenelement vorhanden ist, dessen Städtename oder der Wert seines data-name-Attributs dem Wert des Suchfelds entspricht.
- Wenn ja, bedeutet dies, dass der Benutzer das Wetter für diese Stadt bereits kennt, sodass keine weitere AJAX-Anforderung ausgeführt werden muss. Bei den folgenden Aktionen zeigen wir ihnen eine verwandte Nachricht, löschen den Wert des Suchfelds und geben ihm den Fokus.
Hinweis #1: Wie ich bemerkt habe, gibt die API nichts zurück, wenn Sie nach einer Stadt mit höchstens zwei Buchstaben suchen, die keinen Ländercode darstellen (z. B. athens, aa). Wenn Sie dagegen nach einer Stadt mit mindestens drei Buchstaben suchen, die auch keinen Ländercode darstellen (z. B. athens, aaaa), ignoriert die API den Teil nach dem Komma und gibt alle als "benannten" Städte zurück erster Teil (z. B. Athen).
Hinweis #2: In dieser Übung wird auch nicht auf den Sonderfall eingegangen, in dem ein Land mehr als eine Stadt mit demselben Namen enthält (z. B. Athen in den USA). Wenn ein Benutzer beispielsweise nach "athens, us" sucht, wird nur eine Stadt auf dem Bildschirm angezeigt und nicht mehr. Um das ideale Szenario abzudecken, sollten Benutzer die Stadt-ID irgendwie kennen (z. B. sie möglicherweise als Dropdown-Liste verfügbar machen) und darauf basierend suchen, anstatt anhand ihres Namens zu suchen.
Ausgezeichnete Arbeit, Leute! Wir haben gerade unsere App erstellt. Lassen Sie uns einen Blick darauf werfen:
Schlussfolgerung
Und wir sind fertig! Dies war wirklich eine ziemlich lange Reise, aber ich hoffe, dass es Ihnen gefallen hat und dass es dazu beigetragen hat, Ihre Front-End-Fähigkeiten zu verbessern.
Vergessen Sie nicht, Ihren eigenen Schlüssel für Live-App-Tests einzugeben!
Schauen wir uns zur Erinnerung noch einmal an, wie die App funktioniert:
Wie immer vielen Dank fürs Lesen!
Nächste Schritte
Es gibt so viele Dinge, die Sie tun können, um die Funktionalität dieser App zu erweitern. Hier sind einige Gedanken:
- Verwenden Sie die Geolokalisierung, um den Standort des Benutzers zu ermitteln, und führen Sie dann eine AJAX-Anforderung zum Abrufen von Wetterdaten für die nächstgelegenen Städte aus.
- Verwenden Sie localStorage, um die oben genannten Daten oder sogar eine Echtzeitdatenbank wie Firebase beizubehalten.
- Verwenden Sie eine Diagrammbibliothek wie Highcharts.js, um ein Meteogramm zu erstellen, das eine Wettervorhersage liefert. In diesem Fall kann dieses Tutorial hilfreich sein.
- Verwenden Sie eine Bild-API wie die Flickr-API, um als Galerie-Leuchtkasten eine Liste mit Fotos für jede Stadt anzuzeigen.
Wenn Sie noch etwas als App-Erweiterung sehen möchten, lassen Sie es mich in den Kommentaren unten wissen!