1. Web Design
  2. HTML/CSS
  3. HTML

Erstellen einer einfachen Wetter-App mit Vanilla JavaScript

Scroll to top
Read Time: 13 min

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

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:

Register for an APIRegister for an APIRegister for an API

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:

The available packages for the OpenWeatherMap APIThe available packages for the OpenWeatherMap APIThe available packages for the OpenWeatherMap API

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:

The custom weather icons were going to use from Envato ElementsThe custom weather icons were going to use from Envato ElementsThe custom weather icons were going to use from Envato Elements
Die benutzerdefinierten Wettersymbole, die wir von Envato Elements verwenden werden

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:

The cross-origin error due to the autofocus attributeThe cross-origin error due to the autofocus attributeThe cross-origin error due to the autofocus attribute

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:

The layout of the first section on large screensThe layout of the first section on large screensThe layout of the first section on large screens

Auf kleineren Bildschirmen werden die Formularelemente in zwei Zeilen aufgeteilt:

The layout of the first section on small screensThe layout of the first section on small screensThe layout of the first section on small screens

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.

The unordered list layout on large screensThe unordered list layout on large screensThe unordered list layout on large screens

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.

The card layout

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:

  1. Verhindern Sie das Senden des Formulars und verhindern Sie daher das erneute Laden der Seite.
  2. 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:

  1. 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
  2. Der API-Schlüssel. Auch hier sollten Sie Ihren eigenen Schlüssel verwenden, um unerwartete Fehler aufgrund von API-Aufrufbeschränkungen zu vermeiden.
  3. 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 die json()-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..

An example of the response dataAn example of the response dataAn example of the response data
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:

  1. 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.
  2. 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:

The unordered list layout with the default iconsThe unordered list layout with the default iconsThe unordered list layout with the default icons

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:

Mapping the custom icons with the original onesMapping the custom icons with the original onesMapping the custom icons with the original ones

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.

Identical list itemsIdentical list itemsIdentical list items

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:

Cities that share the same nameCities that share the same nameCities that share the same name

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:

  1. 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.
  2. 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.
  3. 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!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.