Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emelie Nyberg Kedert #413

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# Weather App

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
This project is a weather application that fetches and displays weather data from the OpenWeatherMap API. The main features include displaying the current weather conditions, temperature, and a four-day forecast, as well as providing sunrise and sunset times. The assignment required following a specific design and using JavaScript to retrieve and present the data dynamically.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
The task was to build a weather app using the OpenWeatherMap API, focusing on presenting real-time weather data such as temperature, weather description, sunrise and sunset times, and a 4-day forecast. The main challenges were to fetch the data using JavaScript's `fetch()` function, dynamically inject it into the DOM, and style the app to fit the provided design.

#### Approach
I started by planning how to structure the application and which data points to display from the API. The first step was to set up the API connection using the provided API key and to fetch the weather data in JSON format. I used the `fetch()` method to get the data and implemented error handling with `.catch()` to handle any issues during the fetch request.

To display the weather information, I extracted key elements from the API's JSON response, such as the city name, current temperature, weather description, and sunrise/sunset times. I used JavaScript to inject this information into the HTML elements. I also formatted the sunrise and sunset times into a more readable format using JavaScript's Date object.

For the 4-day weather forecast, I filtered the API data to retrieve information for the same time (12:00) each day. This provided a consistent forecast and avoided overcrowding the display with too many data points.

#### Tools and Techniques
- **JavaScript:** Used `fetch()` to call the OpenWeatherMap API and manipulate the DOM to display the weather data.
- **CSS:** Styled the app to match the provided design. I also added responsiveness to ensure it looks good on devices ranging from 320px to 1600px in width.
- **HTML:** Used semantic elements and classes to structure the content and facilitate dynamic updates with JavaScript.

#### Additional Thoughts
If I had more time, I would have refactored the code to break out the HTML updates from the `fetch()` function into a separate function. This would have improved the code's readability and maintainability. Additionally, I would have liked to create my own design for the app to make it more personalized, as I use weather apps like SMHI frequently. Implementing more stretch goals, such as a city selection feature or dynamically changing the page's colors based on the weather conditions, would have been interesting to explore as well.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://weatherapp-ek.netlify.app/
34 changes: 34 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather app</title>
<link rel="stylesheet" href="style.css">
<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=Roboto:ital,wght@0,300;1,100&display=swap" rel="stylesheet">
</head>

<body>
<div class="weather" id="weather">
<div class="weather-container" id="weatherContainer">
<div class="view-current-weather-icon" id="viewCurrentWeatherIcon"></div>

<div class="view-current-temp" id="viewCurrentTemp"></div>

<div class="view-current-city" id="viewCurrentCity"></div>

<div class="view-current-weather-text" id="viewCurrentWeatherText"></div>

<div class="view-sunrise-sunset" id="viewSunriseSunset"></div>
</div>
</div>

<div class="forecast-container" id="forecastContainer"></div>

<script src="script.js"></script>
</body>

</html>
4 changes: 2 additions & 2 deletions pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## Netlify link
Add your Netlify link here.
PS. Don't forget to add it in your readme as well.

https://weatherapp-ek.netlify.app/
132 changes: 132 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// DOM selectors
const weather = document.getElementById('weather');
const viewCurrentWeatherIcon = document.getElementById('viewCurrentWeatherIcon');
const viewCurrentTemp = document.getElementById('viewCurrentTemp');
const viewCurrentCity = document.getElementById('viewCurrentCity');
const viewCurrentWeatherText = document.getElementById('viewCurrentWeatherText');
const viewSunriseSunset = document.getElementById('viewSunriseSunset');
const forecastContainer = document.getElementById('forecastContainer');

// API
const API_KEY = '7438f6ff1587b542eeec76b91380b575';
const city = 'Stockholm';
const URL_WEATHER = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&APPID=${API_KEY}`;
const URL_FORECAST = `https://api.openweathermap.org/data/2.5/forecast?q=${city}&units=metric&APPID=${API_KEY}`;

const getTodaysWeather = () => {
fetch(URL_WEATHER)
// Reurn API-answere in JSON-format
.then(response => response.json())

.then(data => {
// Get API data
const currentWeatherIcon = data.weather[0].icon;
// Commented out this code, just to show how it looks if temperature is rounded to 1 decimal
//const currentTemp = data.main.temp.toFixed(1);
const currentTemp = Math.round(data.main.temp);
const currentCity = data.name;
const currentWeatherText = data.weather[0].description;

// Convert the Unix timestamp returned by the OpenWeatherMap API to a readable date and time format
const sunriseTime = new Date(data.sys.sunrise * 1000);
const sunsetTime = new Date(data.sys.sunset * 1000);

// To calculate sunset/sunrise
const currentTime = new Date();

// Convert time to 00:00 format
const sunriseTimeFormatted = sunriseTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const sunsetTimeFormatted = sunsetTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });

// When background for night/day should me shown
if (currentTime >= sunriseTime && currentTime < sunsetTime) {
weather.classList.add('daytime');
} else {
weather.classList.add('nighttime');
}

// Update HTML
viewCurrentWeatherIcon.innerHTML = `
<img src="https://openweathermap.org/img/wn/${currentWeatherIcon}@2x.png" alt="Weather icon">
`;
viewCurrentTemp.innerHTML = `
<h1>${currentTemp}</h1><h2>°C</h2>
`;
viewCurrentCity.innerHTML = `
<h3>${currentCity}</h3>
`;
viewCurrentWeatherText.innerHTML = `
<h4>${currentWeatherText}</h4>
`;
viewSunriseSunset.innerHTML = `
<h4>sunrise</h4>
<h4>${sunriseTimeFormatted}</h4>
<h4>sunset</h4>
<h4>${sunsetTimeFormatted}</h4>
`;
})

// Catch logs and errors
.catch((error) => {
console.log('Error:', error);
});
}

// Call the function to get and show todays weather
getTodaysWeather();

const getWeatherForecast = () => {
fetch(URL_FORECAST)
.then(response => response.json())

.then(data => {
// Get unique days in the forecast (filtering by date without time)"
const days = {};
data.list.forEach(item => {
const date = item.dt_txt.split(' ')[0]; // Get only date
if (!days[date]) {
days[date] = []; // Create array for each date
}
days[date].push(item); // Add all weather entries for each date
});

// Skip first day (index 0)
const filteredDays = Object.keys(days).slice(1, 6);

// Loop through filtred days and show the data
filteredDays.forEach(date => {
const dayForecast = days[date];

// Find highest and lowest temperature for each day
const temps = dayForecast.map(item => Math.round(item.main.temp));
const maxTemp = Math.max(...temps);
const minTemp = Math.min(...temps);

// Get weahter icon at 12:00 for the specific day
const middayForecast = dayForecast.find(item => item.dt_txt.includes('12:00:00'));
const weatherIcon = middayForecast ? middayForecast.weather[0].icon : dayForecast[0].weather[0].icon;

// Get the weekday (three letters)
const dayOfWeek = new Date(date).toLocaleDateString('en-GB', { weekday: 'short' });

// Build HTML for every days forecast
forecastContainer.innerHTML += `
<div class="forecast">
<p>${dayOfWeek}</p>
<div class="forecast-icon-temp">
<img src="http://openweathermap.org/img/wn/${weatherIcon}.png" alt="Weather icon">
<p>${minTemp}° / ${maxTemp}°C</p>
</div>
</div>
`;
});
})

// Catch logs and errors
.catch(error => {
console.error('Error:', error);
});
}

// Call the function to get and show weather forecast
getWeatherForecast();
157 changes: 157 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
body {
background-color: white;
font-family: "Roboto", sans-serif;
font-weight: 300;
font-style: normal;
margin: 0;
}

h1 {
font-size: 120px;
margin: 0;
}

h2 {
font-size: 40px;
font-weight: 400;
margin-top: 15px;
}

h3 {
font-size: 34px;
margin: 0;
}

h4 {
font-size: 22px;
margin: 0;
}

p {
color: #707070;
font-size: 20px;
font-weight: 400;
}

.weather-container {
color: white;
margin: 0 30px;
position: relative;
}

/* Day background styling (lightblue) */
.daytime {
background: linear-gradient(to bottom right, #8589FF, #E8E9FF);
clip-path: ellipse(130% 100% at 50% 0%);
}

/* Night background styling (darkblue) */
.nighttime {
background: linear-gradient(to bottom right, #6264A2, #222350);
clip-path: ellipse(130% 100% at 50% 0%);
}

/* Position of big weather icon */
.view-current-weather-icon {
position: absolute;
right: 70px;
}

/* Icon size */
.view-current-weather-icon img {
width: 200%;
}

.view-current-temp {
padding-top: 131px;
display: flex;
flex-direction: row;
}

/* View weather text with capitals */
.view-current-weather-text {
text-transform: capitalize;
}

.view-sunrise-sunset {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 10% 0 15% 0;
}

.forecast-container {
margin: 0 30px;
margin-top: 20%;
margin-bottom: 10%;
}

.forecast {
display: flex;
flex-direction: row;
justify-content: space-between;
}

/* Styling for icon and temperature to be to the right */
.forecast-icon-temp {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 50%;
}

/* Make icon not streach */
.forecast-icon-temp img {
object-fit: contain;
}

/* For tablets */
@media (min-width: 668px) and (max-width: 1024px) {

.weather-container {
margin: 0 100px;
}

.view-current-weather-icon {
right: 100px;
}

.view-current-weather-icon img {
width: 300%;

}

.forecast-container {
margin: 100px 100px;
}

.forecast {
margin: 3% 0;
}
}

/* For screens */
@media (min-width: 1025px) {

body {
display: flex;
justify-content: space-between;
}

.weather-container {
margin: 0 100px;
}

.daytime,
.nighttime {
width: 50%;
clip-path: none;
height: 100vh;
}

.forecast-container {
width: 50%;
margin: 131px 100px;
}

}