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 to fetch the current weather information.
Our app will display the user's city, country, temperature, and humidity.
Demo app
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 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. `
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 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:
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 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 😃
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 if you have an existing account
- Click on your username at the top right corner and select My API Keys from the dropdown
Your API key will be displayed as seen below. Copy and paste it into your notepad or any text editor on your computer.
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.
Wrapping up 😎
Since we have copied the necessary files from OpenWeather, 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")
}
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? 🤔
Let’s fix that by writing this line of code
arrowBack.addEventListener("click", ()=>{
wrapper.classList.remove("active")
})
Output
Pat yourself at the back for making it thus far 😍 🥰
Feel free to share to help someone ☺️
Top comments (0)