Build a weather app using JavaScript - step by step tutorial 🚀
Tags: javascript, programming, tutorial, api
Published: 2022-09-17 16:36:14

If you are familiar with programming, you will agree that one of the important concepts you must learn is API. 

API (Application Programming Interface) makes it easy for us to communicate with third-party libraries, files, and get access to data.

In this tutorial, I will show you how to build a beautiful weather app using HTML, CSS, and JavaScript. We’ll use the [Open Weather API](https://openweathermap.org/api) to fetch the current weather information. 

Our app will display the user's city, country, temperature, and humidity. 

**Demo app**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2f1w8fasxl66h2nmo5b.gif)


**The tutorial covers the following:** 

- Text validation
- CSS styling
- JSON to string
- Geolocation
- API 
& more…

Let’s dive into it. 

`**Step 0 – Setting up the project**

Open the IDE or code editor of your choice. Create a new folder, I will name mine weather app. For the sake of this tutorial, I will be using Visual Studio Code. Moreso, I provided the [image link](https://github.com/devjaiye/js-weather-app/tree/master/icons) for this tutorial. 

We need to create our blank files `index.html`, `style.css`, and `script.js`. Create a folder in your project to store the images we will use. `

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5new0ouho4ht0dcseqll.png)

It's time to write the basic HTML code to setup your project. In the code below, I changed the title name to "Weather App". 

`index.html`

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Weather App</title>
</head>
<body>
    

</body>
</html>
```

**Add the CSS link and Box Icon**

After the `</title>` closing tag, we can write the code to connect our css file, and Box Icon link. The [Box Icon CDN](https://boxicons.com/usage) will be used to generate icons in our app. Next, we can link the JavaScript file below the `</div>` tag.

`index.html`

```
</title>
    <!-- css file -->
    <link rel="stylesheet" href="style.css">
    <!-- Box Icon plugin -->
    <link href="https://unpkg.com/boxicons@2.0.9/css/boxicons.min.css" rel="stylesheet">
</head>
<body>
    <div class="wrapper">
 <!-- Your code here -->
    </div>
    <!-- Js file -->
    <script src="script.js"></script>
```


**Create The HTML Layout for the app** 

Inside the `<body>` tag of our `index.html` file, we need to create a `<div>` with a class name of `wrapper`

```
<div class="wrapper">

        <!-- your code here -->
    
    </div>
```
  
In the `<header>` tag, let us create a left arrow icon (<-). The icon is generated from Box Icon. Then, give your app a preferred name. Mine is “Weather App” 

```
<div class="wrapper">
        <!-- your code here -->
	 <header><i class="bx bx-left-arrow-alt"></i>Weather App</header>
    </div>
```
We need to create two `<section>` tags inside our `<div>`. The first `<section>` will contain our input and button

```
<div class="wrapper">
        <div class="wrapper">
            <header><i class="bx bx-left-arrow-alt"></i>Weather App</header>
            <!-- input and button -->
            <section class="input-part">
                <p class="info-text"></p>
                <input type="text" placeholder="Enter city here..." spellcheck="false" required>
                <div class="separator"></div>
                <button>Get Device Location</button>
            </section>
    </div>
```

At the end of the `</section>` tag, we can write the code for the second `<section>` to display the weather information.  

```
</section>
<!-- display weather info -->
        <section class="weather-part">
            <img src="icons/cloud.svg" alt="weather icon">
            <div class="temp">
                <span class="numb">_</span> <span class="deg">°</span>C
            </div>

            <div class="weather">_ _</div>
            <div class="location">
                <i class="bx bx-map"></i>
                <span>_, _</span>
            </div>
            <div class="bottom-details">
                <div class="column feels">
                    <i class="bx bxs-thermometer"></i>
                    <div class="details">
                        <div class="temp">
                            <span class="numb-2">_</span>
                            <span class="deg">°</span>C
                        </div>
                        <P>Feels like</P>
                    </div>
                </div>

                <div class="column humidity">
                    <i class="bx bxs-droplet-half"></i>
                    <div class="details">
                            <span>_</span>
                            <p>Humidity</p>
                        </div>
                    </div>
                </div>
            </div>
        </section>
	</div>
```

This is everything you need to do inside the `<div>`

 The result of our code will look as so: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oa0nyqnuogbwgi6wu0lr.gif)

_Note: the Box Icon will not be generated if your internet connection is not connected._ 

**Step 1 – Styling our HTML code in style.css**

Our HTML code looks ugly, we need to make the best out of it using CSS. Open your style.css file and let’s get it done. 
We will start by adding a few changes to the HTML code.The asterisk (*) sign in CSS is useful when you want to set a style for all the elements of an HTML page or for all of the elements within an element of an HTML page. The (*) class will contain the following attributes: `margin`, `padding`, `box-sizing`, `font-family`

`style.css`
```
*{
    margin: 0; 
    padding: 0; 
    box-sizing: border-box;
    font-family: 'Segoe UI', Tahoma, Verdana, sans-serif;
}
```
**Styling the `<body>`, `<div>`, classes and elements** 

The code below will be used to style the body of our HTML code. 

```
body{
    display: flex;
    min-height: 100vh;
    align-items: center;
    justify-content: center;
    background-color: rgb(86, 11, 165);
}
```
Now, it's time to style our `wrapper`, elements and classes in our first `<section>` for the input.  Next, we have to style the second <section> which houses the weather details of our app. 

```
.wrapper{
    width: 400px;
    border-radius: 7px;
    background-color: #fff;
}

.wrapper header{
    color: rgb(86, 11, 165);
    font-size: 21px;
    font-weight: 500;
    padding: 16px 16px;
    display: flex;
    align-items: center;
    border-bottom: 1px solid #ededed;
}

header i{
    cursor: pointer;
    font-size: 0px;
    margin-right: 8px;
    color: #000;
}

.wrapper.active header i{
    font-size: 30px;
    margin-left: 5px;
}

.wrapper .input-part{
    margin: 20px 25px 30px;
}

.wrapper.active .input-part{
    display:none;
}

.input-part :where(input, button){
    width: 100%;
    height: 55px;
    border: none;
    outline: none;
    font-size: 18px;
    border-radius: 7px;
}

/* input required style */
.input-part input::is(:focus, :valid){
    border: 2px solid #43AFFC;
}

.input-part input{
    text-align: center;
    border: 1px solid #bfbfbf;
}

.input-part .info-text{
    display: none;
    margin-bottom: 15px;
    padding: 12px 10px;
    text-align: center;
    border-radius: 7px;
    font-size: 17px;
}

.info-text.error{
    display: block;
    color: #721c24;
    background-color: #f8d7da;
    border: 1px solid #f5c6cb;
}

.info-text.pending{
    display: block;
    color: #0c5460;
    background: #d1ecf1;
    border: 1px solid #f5c6cb;
}

.input-part .separator{
    height: 1px;
    width: 100%;
    margin: 20px 0;
    background: #ccc;
    display: flex;
    align-items: center;
    justify-content: center;
}

.separator::before{
    content: "or";
    color: #ccc;
    font-size: 19px;
    padding: 0 15px;
    background: #fff;

}

.input-part button{
    color: #fff;
    cursor: pointer;
    background: rgb(86, 11, 165);
}

/* weather style */
.wrapper .weather-part{
    margin: 30px 0 0;
    display: none;
    align-items: center;
    justify-content: center;
    flex-direction: column;
}

.wrapper.active .weather-part{
    display: flex;
}

.weather-part img{
    max-width: 125px;
}

.weather-part .temp{
    display: flex;
    font-size: 72px;
    font-weight: 500;
}

.weather-part .temp .numb{
    font-weight: 600;
}

.weather-part .temp .deg{
    font-size: 40px;
    margin: 10px 5px 0 0;
    display: block;
}

.weather-part .weather{
    font-size: 21px;
    text-align: center;
    margin: -5px 20px 15px;
}

.weather-part .location{
    display: flex;
    align-items: center;
    font-size: 21px;
    margin-bottom: 30px;
}

.location i{
    font-size: 22px;
    margin-right: 5px;
}

.weather-part .bottom-details{
    width: 100%;
    display: flex;
    align-items: center;
    border-top: 1px solid #bfbfbf;
    justify-content: space-between;
}

.bottom-details .column{
    width: 100%;
    display: flex;
    padding: 15px 0;
    align-items: center;
    justify-content: center;
}

.column i{
    color: #43AFFC;
    font-size: 40px;
}

.column.humidity{
    border-left: 1px solid #bfbfbf;
}

.details .temp, .humidity span{
    font-size: 18px;
    font-weight: 500;
    margin-top: -3px;
}

.details .temp .deg{
    margin: 0;
    font-size: 17px;
    padding: 0 2px 0 1px;
}

.details p{
    font-size: 14px;
    margin-top: -6px;
}
```
Once you complete the CSS code your result should look like the image below 😃

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5hy75gknws6airbr4xdb.png)

We are almost there. Let’s add some functionality to our app using JavaScript.

**Step 2 - Add Functionality to the app**

Finally, we are at the last part. Open the `script.js` file.
The first thing we need to do inside the `script.js` file is to find the HTML elements based on their tag name and class. 

`script.js`

```
const wrapper = document.querySelector(".wrapper")
inputPart = document.querySelector(".input-part")
infoText = document.querySelector(".info-text")
inputField = document.querySelector("input")
getLocationBtn = document.querySelector("button")
arrowBack = wrapper.querySelector("header i")
weatherIcon = document.querySelector("weather-part img")
```
 **API Key**
Before we proceed further, you will need an API key to get the weather information. 

Follow the steps below To get the API key: 

- sign up or login to [OpenWeather](https://openweathermap.org) if you have an existing account 
- Click on your username at the top right corner and select **My API Keys** from the dropdown

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqmio7wnyvtnh9ub4dv3.gif)

Your API key will be displayed as seen below. Copy and paste it into your notepad or any text editor on your computer.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o419e89a5uczfrffnea0.png)

**Open Weather API** 👍

We are still on the website, time to get the weather API for our project. The process for that is explained below. Copy and paste the API into your text editor.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0px8ewy1tqeisq7cead9.gif)

**Wrapping up 😎**

Since we have copied the necessary files from [OpenWeather](https://openweathermap.org), we can go back to the `script.js` file to make use of the code. 

We need to create two variables, one for the API and the other for the API key. 

```
let api
var apiKey = "paste the api key here…"
```

Next, let's create functions to handle our API requests. Firstly, I will create a function which will request the API from the server. For the API to search the weather information by cities, it must be specified in the code as seen below.

`function requestApi()`

```
function requestApi(city){
    //..addded {&units=metric} to the api to round up the number to the nearest 
    api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`
    fectchWeatherData()
}
```
`fectchWeatherData()` This will display an information to notify the user about the process of the app in a `<p>`tag. Thereafter, we can get the data from the server and return the API response by passing it into our app as a JavaScript object.  

`.then()` calls the `weatherDetails()` where we pass in our result.

`fectchWeatherData()`
```
function fectchWeatherData(){
    infoText.innerText="Getting weather info..."
    infoText.classList.add("pending")
//...get server and return api response
    fetch(api).then(response => response.json()).
    then(result => weatherDetails(result)) 
}
```
`weatherDetails()`

```
function weatherDetails(info){
    infoText.classList.replace("pending", "error") //..our css style changes the text info background 
    if(info.cod == "404"){ //..cod is an object called from the weather api
        infoText.innerText = `You entered ${inputField.value} which isn't a valid city` //..checks for validation
    } else{
         //..get api data to properties in info-text   
        const city = info.name
        const country = info.sys.country
        const {description, id} = info.weather[0]
        const{feels_like,humidity, temp} = info.main

        //..parse the above values into the html elements 
        wrapper.querySelector(".temp, .numb").innerText = Math.floor(temp) //..round up number to nearest Integer
        wrapper.querySelector(".weather").innerHTML = description
        wrapper.querySelector(".location span").innerHTML = `${city}, ${country}`
        wrapper.querySelector(".temp .numb-2").innerHTML = Math.floor(feels_like)
        wrapper.querySelector(".humidity span").innerHTML = `${humidity}%`
       infoText.classList.remove("pending", "error") //..if we get the correct city from the api we hide pending and error message
        wrapper.classList.add("active") //..show the dashboard which displays the weather info
    }
}
```
**Get user’s current location** 

We can make our app detect the user’s current location whenever the button is clicked, and the permission is accepted by the user.

```
getLocationBtn.addEventListener("click", ()=>{
    if(navigator.geolocation){ //..if user's browser supports geolocation
        navigator.geolocation.getCurrentPosition(onSuccess, onError)
    } else {
        alert("Browser doesn't support geolocation api")
    }
})
```
 If the user grants the request, we'll get the weather details of the user's location. Else an error message will be displayed if the user declines. 

To perform this we can write the code in a function called `onSuccess()` and `onError()`

```
function onSuccess(position){
    const {latitude, longitude} = position.coords //..getting the lat and long from coordinator object
    api = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=metric&appid=${apiKey}`
    //..addded {&units=metric} to the api to round up the number
    fectchWeatherData()
}

function onError(error){
  infoText.innerText = error.message //..html text will display error message
  infoText.classList.add("error")
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwojrhqdcll14qk0duyw.gif)

**The Input Field**

When the user type an input and press the enter button on the keyboard the app will fetch the weather information from the server.

```
inputField.addEventListener("keyup", e=>{
    //... input value is not empty 
    if(e.key == "Enter" && inputField.value !=""){
        requestApi(inputField.value)
    }
})
```
The app fulfills its purpose if you run the code, But did you notice the back arrow isn’t working? 🤔

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkyrgg0qx061wl3n4fp0.gif)

Let’s fix that by writing this line of code 

```
arrowBack.addEventListener("click", ()=>{
    wrapper.classList.remove("active")
})
```

**Output**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56gjxbgj0u2h3ilvdnl1.gif)

Pat yourself at the back for making it thus far 😍 🥰

Feel free to share to help someone ☺️

[Get the code](https://github.com/devjaiye/js-weather-app) 🚀