Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config.js
node_modules
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
# Weather Forecast App

A clean, responsive weather application that displays current conditions and a 5-day forecast for any city using the OpenWeatherMap API.

## Features

- Current weather conditions with temperature and description
- 5-day forecast with daily high temperatures
- Responsive design (mobile and desktop)
- Real-time data from OpenWeatherMap API
- Clean, minimal interface

## Technologies

- JavaScript
- HTML5
- CSS3
- Bootstrap 5
- OpenWeatherMap API
- Google Fonts (Montserrat)

## What I Learned

- Working with external APIs and data fetching
- DOM manipulation and dynamic content rendering
- Responsive design with Bootstrap grid system
- Date formatting with Intl.DateTimeFormat
- Managing API keys with config files

## Future Enhancements

- After a user has searched a city (and that city's weather information is currently displaying), the user should see a "Set as Default" button. When the user clicks this button, there should be some indication that the current city is set as their default city. If the user refreshes the page, instead of a blank screen and search bar, their default city's info should come up.
- Add a button to the interface that will allow the user to automatically look up their current location using the Geolocation API
- Alter the styling based on the weather type to give the user a different experience based on the weather of their selected city
- Place a map on the page with the current location shown using the Google Maps Embed API
- Add in nearby cities to the map with the current temperature and weather shown on the map "pin" and let them click a "pin" to switch to that city's weather.

## Weather Project

This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks.
Expand Down
3 changes: 3 additions & 0 deletions config.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const CONFIG = {
API_KEY: 'YOUR_API_KEY_HERE', // Get your key from openweathermap.org
};
55 changes: 55 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
<title>Weather forecast app</title>
</head>
<body>
<div class="container my-5 py-3">
<div class="row justify-content-center">
<div class="col-12 col-md-6">
<div class="text-center mb-4">
<h1>Weather Forecast</h1>
<hr />
</div>

<form id="search-form">
<div class="input-group mb-4 py-3">
<input
type="text"
id="search-query"
class="form-control"
placeholder="Enter City Name Here"
/>
<button type="submit" class="btn btn-primary search">
Search
</button>
</div>
</form>

<div class="weather row justify-content-center"></div>
</div>
</div>
</div>

<div class="container mt-4">
<div class="forecast row g-3 justify-content-center"></div>
</div>
<script src="config.js"></script>
<script src="main.js"></script>
</body>
</html>
122 changes: 122 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
const { API_KEY } = CONFIG;
let weather = [];
let forecast = [];

document.querySelector('#search-form').addEventListener('submit', (event) => {
event.preventDefault();
const input = document.querySelector('#search-query');
const city = input.value.trim();
if (!city) {
return;
}
input.value = '';
fetchWeatherData(city);
fetchForecastData(city);
});

const fetchWeatherData = (city) => {
const url = new URL('https://api.openweathermap.org/data/2.5/weather');
url.searchParams.set('q', city);
url.searchParams.set('units', 'imperial');
url.searchParams.set('appid', API_KEY);
fetch(url)
.then((data) => data.json())
.then((data) => addCurrentWeather(data));
};

const addCurrentWeather = (data) => {
weather = [];
const weatherData = {
temperature: Math.round(data.main.temp),
city: data.name,
weather: data.weather[0].main,
icon: data.weather[0].icon,
};
weather.push(weatherData);
renderCurrentWeather();
};

const fetchForecastData = (city) => {
const url = new URL('https://api.openweathermap.org/data/2.5/forecast');
url.searchParams.set('q', city);
url.searchParams.set('units', 'imperial');
url.searchParams.set('appid', API_KEY);
fetch(url)
.then((data) => data.json())
// .then((data) => console.log(data));
.then((data) => addForecast(data));
};

const addForecast = (data) => {
// Re-initialize forecast every search
forecast = [];
const weekDay = new Intl.DateTimeFormat('en-us', {
weekday: 'long',
});
for (let day = 0; day < 5; day += 1) {
// API gives data every 3 hours, 8 entries is 1 day, I offset it to aim for daytime and skip having multiple same day entries, but the limitation is that the start time varies, so I cannot guarantee daytime icons with my code ablilities right now
const index = day * 8 + 4;
const forecastDay = {
weather: data.list[index].weather[0].main,
temperature: Math.round(data.list[index].main.temp),
icon: data.list[index].weather[0].icon,
day: weekDay.format(new Date(data.list[index].dt_txt)),
};
forecast.push(forecastDay);
}
renderForecast();
};

const renderCurrentWeather = () => {
document.querySelector('.weather').replaceChildren();
for (let i = 0; i < weather.length; i += 1) {
const weatherData = weather[i];
const template = `
<div class="col-12 col-md-8">
<div class="bg-transparent p-4">
<div class="row align-items-center text-center">
<div class="col-12 col-md-6">
<h2>${weatherData.city}</h2>
<h2>${weatherData.temperature}°</h2>
<h3>${weatherData.weather}</h3>
</div>
<div class="col-12 col-md-6">
<img
src="https://openweathermap.org/img/wn/${weatherData.icon}@2x.png"
alt="Weather icon"
class="mx-auto d-block"
/>
</div>
</div>
</div>
</div>
`;
document
.querySelector('.weather')
.insertAdjacentHTML('beforeend', template);
}
};

const renderForecast = () => {
document.querySelector('.forecast').replaceChildren();
for (let i = 0; i < forecast.length; i += 1) {
const forecastData = forecast[i];
const template = `
<div class="col-6 col-md-2">
<div class="card shadow-sm bg-transparent text-center py-4 h-100">
<h4>${forecastData.day}</h4>
<img
src="https://openweathermap.org/img/wn/${forecastData.icon}@2x.png"
alt="Forecast icon"
class="mx-auto d-block"
/>
<h4>${forecastData.temperature}°</h4>
<small>${forecastData.weather}</small>
</div>
</div>
`;
document
.querySelector('.forecast')
.insertAdjacentHTML('beforeend', template);
}
};
Loading