Monday, December 23, 2019

Build a Simple Weather App With Vanilla JavaScript

Final product image
What You'll Be Creating

In today’s tutorial, we’ll learn how to build a simple, yet fully functional weather app with Vanilla JavaScript. We have a lot of interesting things to cover, so grab a cup of coffee and let’s get started!

What We’ll be Building

Here’s an introductory video which demonstrates the functionality of the app that we’re going to create:

Here’s the demo on CodePen for you to fork and play with:

Note: This tutorial assumes that you’re familiar with AJAX, an essential front-end technique. If you’re just beginning, check out this series.

1. Scaffolding the App

Before start creating our app, there are a few things that we have to take into consideration.

Find a Weather API

First things first, we have to find a provider that will let us incorporate its weather data into our app. Luckily enough, there are several different providers out there for developing weather apps. Most of them include a free package along with premium subscriptions that scale depending on the services/features. 

In our case, we’re going to use OpenWeatherMap, one of the most popular free choices. To take advantage of its capabilities, first, we have to sign up for an API key:

Register for an API

This service comes with different packages. As you can see from the visualization below, the starter (free) one allows 60 calls per minute which suits our needs:

The available packages for the OpenWeatherMap API

So before continuing, please make sure that you’ve registered for an API key. Later, we’ll include that key in our script. 

Keep in mind that the best way to test the app is by forking the Codepen demo and including your own key. If we all share the same key, the app will probably not work due to API call limits.

Find Weather Icons

Next, for the purposes of this tutorial, we’re going to need a bunch of weather icons. It’s worth noting that OpenWeatherMap comes with its own icon set and we’ll take a look at those. However, we’ll go one step further and use some custom ones. 

Once more, we’ll take advantage of the Envato Elements library and download a pack of vector weather icons:

The custom weather icons were going to use from Envato Elements
The custom weather icons we’re going to use from Envato Elements

2. Define the Page Markup

We’ll define two sections. 

The first section will include a heading, a search form, and an empty span element. This element will become visible with an appropriate message under certain conditions. Specifically, if there isn’t any weather data available for a requested city or the data for this city are already known. 

The second section will include a list of cities. By default, it won’t contain any cities. But, as we start searching for the weather for a specific city, if weather data is available, a corresponding list item (city) will be appended to the unordered list.

Here’s the initial page markup:

Note: In our Codepen demo, the autofocus attribute of the search field won’t work. In fact, it’ll throw the following error which you can see if you open your browser console:

The cross-origin error due to the autofocus attribute

However, if you run this app locally (not as a Codepen project), this issue won’t exist.

And here’s the markup associated with a list item that we’ll generate dynamically through JavaScript:


2. Specify Some Basic Styles

With the markup for the app ready, we’ll forge on with the CSS. The first step, as always, is to specify some CSS variables and common reset styles:


4. Set the Main Styles

Let’s now discuss the main styles of our app.

Section #1 Styles

First, we’ll add some straightforward styles to the elements of the first section. 

On medium screens and above (>700px) the layout should look like this:

The layout of the first section on large screens

On smaller screens the form elements will split into two lines:

The layout of the first section on small screens

Here are the associated styles:

Section #2 Styles

We’ll use CSS Grid to lay out the list items. Remember that each list item will represent a city. Their width will depend on the screen size.

On large screens (>1000px) we’ll have a four column layout.

The unordered list layout on large screens

Then on medium screens (>700px and ≤1000px) a three column layout, on small screens (>500px and ≤700px) a two column layout, and finally on extra small screens (≤500px) all elements will be stacked.

Here are the corresponding styles:

Each column will look like a card with a bottom shadow that will be added via the ::after pseudo-element. 

Inside the card, we’ll place weather information about the requested city. These will come from our request, apart from the icons. Those icons, which as mentioned above are grabbed from Envato Elements, will show the current weather condition of this city and match the equivalent OpenWeatherMap icons.

The card layout

Below you can see a part of the CSS needed for this layout:


5. Add the JavaScript

At this point, we’re ready to build the core functionality of our app. Let’s do it!

On Form Submission

Each time a user submits the form by pressing the Enter key or the Submit button, we’ll do two things:

  1. Stop the form from submitting, hence prevent reloading the page.
  2. Grab the value which is contained in the search field.

Here’s the starting code:

Next, we’ll check to see whether there are list items (cities) inside the second section. 

Perform an AJAX Request

We’ll start with the assumption that the list is empty. That said, it has never run any AJAX request in the past. In such a case, we’ll execute a request to the OpenWeatherMap API and pass the following parameters:

  1. The city name (e.g. athens) or the comma-separated city name along with the country code (e.g. athens,gr) which will be the value of the search field
  2. The API key. Again, you should use your own key to avoid unexpected errors due to API call limits.
  3. The unit of temperature for the requested city. In our case, we’ll go with Celcius.

With all the above in mind, by following the API documentation, our request URL should look something like this:

To perform the AJAX request, we have a lot of options. We can use the plain old XMLHttpRequest API, the newer Fetch API, or even a JavaScript library like jQuery and Axios. For this example, we’ll go with the Fetch API. 

To grab the desired data, we have to do the following things:

  • Pass the URL we want to access to the fetch() method. 
  • This method will return a Promise containing the response (a Response object). But this won’t be the actual response, just an HTTP response. To grab the response data in the desired JSON format (this is the default data format of OpenWeatherMap), we’ll use Response object’s json() method.
  • This method will return another Promise. When it’s fulfilled, the data will be available for manipulation.
  • If for some reason the request is unsuccessful, a corresponding message will appear on the screen.

So, our AJAX request would look something like this:

Tip: Instead of chaining then()s, we could have used the newer and more readable async/await approach for the AJAX request.

Here’s an example of the response data:

An example of the response data

Build the List Item Component

With the AJAX request in place, each time we type a city in the search field, the API will return its weather data, if they are available. Our job now is to collect only the data that we need, then create the associated list item and, lastly, append it to the unordered list.

Here’s the code responsible for this job:

There are two things here we have to discuss:

  1. If you look again at the response visualization above, you’ll notice that the API returns an icon code (e.g. "50d") which holds the current weather condition for the target city. Based on this code, we’re able to construct the icon URL and display it in the card via the img tag.
  2. Inside the .city-name element of each list item, we’ll append the data-name attribute with value the cityName,countryCode (e.g. madrid,es). Later we’ll use this value to prevent duplicate requests.

Reset Things

Lastly, after the AJAX request, we’ll clear the content of the .msg element, the value of the search field, and give focus to that field as well:

Great job, folks! We’ve just created the first version of our app. By the time you put your own API key and search for a city, you should see a card layout similar to that one:

The unordered list layout with the default icons

Here’s the related Codepen demo:

Add Custom Icons

Let’s now customize a little bit the look and feel of our app. We’ll replace the default OpenWeatherMap PNG icons with the SVGs we downloaded earlier from Envato Elements.

To do this, I’ve uploaded all the new icons to Codepen (via the Asset Manager as I’m a PRO member) and changed their names, so they will match the names and the weather conditions of the original icons, like this:

Mapping the custom icons with the original ones

Then, in the code, we only have to change the icon path:

Prevent Duplicate Requests

There’s still one thing we have to fix. So far, as we perform a successful AJAX request, a list item is created. That said, the list can contain multiple identical list items which refer to the same city, like so:

Identical list items

That’s bad user experience, so let’s make sure that only a single request is triggered for a specific city. 

But before that, there’s another thing for taking into consideration. The same city name can exist in more than one country. For example, if we search for “Athens” in the OpenWeatherMap’s search finder, we’ll see these results:

Cities that share the same name

With all the above in mind, we’ll write some code which will ensure that only a single request per city, per country will be executed:

Let me explain what actions happen here:

  1. Again during the submit handler, before making an AJAX request, we check to see whether the unordered list is empty or not. If it isn’t empty, that means at least one successful AJAX request has already been executed. 
  2. Next, we check to see if there’s a list item who’s the city name or the value of its data-name attribute are equal to the search field’s value.
  3. If so, that means the user already knows the weather for this city, so there’s no need to perform another AJAX request. As the following actions, we’ll show them a related message, clear the value of the search field and give it focus.

Note #1: As I’ve noticed, in case you search for a city with at most two-letters which don’t represent any country code (e.g. athens,aa), the API won’t return anything. On the other hand, if you search for a city along with at least three-letters which also don’t represent any country code (e.g. athens,aaaa), the API will ignore the part after the comma and return all cities named as the first part (e.g. athens).

Note #2: For this exercise, we won’t also cover the special case where a country contains more than one city with the same name (e.g. Athens in USA). So, for example, if a user searches for “athens,us” only one city will appear in the screen and not more. In order to cover the ideal scenario, users should somehow know the city ID (e.g. perhaps make them available as a dropdown) and search based on that instead of searching based on its name.

Excellent job, folks! We’ve just built our app. Let’s take a look:


Conclusion

And we’re done! This really was quite a long journey, but I hope that you enjoyed it and that it has helped enhance your front-end skills.

Once again, don’t forget to put your own key for live app testing! 

As a reminder, let’s look again at how the app works:

As always, thanks a lot for reading!

Next Steps

There are so many things that you can do to extend the functionality of this app. Here are some thoughts:

  • Use geolocation to grab the user’s location, and then perform an AJAX request for retrieving weather data for their closest cities.
  • Use localStorage to persist the data above or even a real-time database like Firebase.
  • Use a charting library like Highcharts.js for building a meteogram that will give a weather forecast. If you do so, this tutorial might help.
  • Use an image API like Flickr API to present as a gallery lightbox a list of photos for each city.

If there’s anything else that you might want to see as an app extension, let me know in the comments below! 

No comments:

Post a Comment