diff --git a/README.md b/README.md index f8b15f4cb..db0ea60c5 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # 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. +The project involved building a weather app based on a specific design to follow. ## 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? +If I had more time, I would have improved the search function and added it to the section for current weather, which I did not get to work, it therefore does not work optimally in the responsive design for tablets except ipad air ## 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://zippy-haupia-4b1a8a.netlify.app \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 000000000..76ded57a7 --- /dev/null +++ b/app.js @@ -0,0 +1,172 @@ +// API key for OpenWeatherMap +const API_KEY = 'fb560f0e3d208f655263c202ebe8452d'; + +// URLs for current weather and 5-day forecast +const CURRENT_WEATHER_URL = 'https://api.openweathermap.org/data/2.5/weather'; +const FORECAST_URL = 'https://api.openweathermap.org/data/2.5/forecast'; + +// Default city +let city = 'Stockholm'; + +// DOM selector for current weather and forecast +const currentWeatherElement = document.getElementById('currentWeather'); +const forecastElement = document.getElementById('forecast'); + +// Array of weekday names +const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + +// search icon and input field +const searchIcon = document.getElementById('searchIcon'); +const cityInput = document.getElementById('cityInput'); +const searchContainer = document.querySelector('.search-container'); + + +searchIcon.addEventListener('click', function () { + searchContainer.classList.toggle('active'); + if (searchContainer.classList.contains('active')) { + cityInput.focus(); + } else { + cityInput.blur(); + } +}); + +// Trigger the search +cityInput.addEventListener('keydown', function (event) { + if (event.key === 'Enter') { + const cityInputValue = cityInput.value.trim(); + if (cityInputValue) { + city = cityInputValue; + fetchWeatherData(city); + fetchForecastData(city); + cityInput.value = ''; + } else { + alert('Please enter a city name.'); + } + } +}); + +// Function to convert time format +function convertUnixToTime(unixTime) { + const date = new Date(unixTime * 1000); + const hours = date.getHours(); + const minutes = date.getMinutes(); + return `${hours}:${minutes < 10 ? '0' : ''}${minutes}`; +} + +// Function to change background image based on the weather +function setBackground(weatherDescription, isDaytime) { + if (isDaytime) { + if (weatherDescription.includes('clear')) { + currentWeatherElement.style.backgroundImage = "url('daytime-clear.jpg')"; + } else if (weatherDescription.includes('clouds')) { + currentWeatherElement.style.backgroundImage = "url('daytime-cloudy.jpg')"; + } else { + currentWeatherElement.style.backgroundImage = "url('daytime.jpg')"; + } + } else { + if (weatherDescription.includes('clear')) { + currentWeatherElement.style.backgroundImage = "url('night-clear.jpg')"; + } else if (weatherDescription.includes('clouds')) { + currentWeatherElement.style.backgroundImage = "url('night-cloudy.jpg')"; + } else { + currentWeatherElement.style.backgroundImage = "url('night.jpg')"; + } + } + currentWeatherElement.style.backgroundSize = "cover"; +} + +// Function to fetch current weather +function fetchWeatherData(city) { + fetch(`${CURRENT_WEATHER_URL}?q=${city}&units=metric&APPID=${API_KEY}`) + .then(response => response.json()) + .then(data => { + console.log('Current Weather:', data); + + // Data from the response + const location = data.name; + const temperature = Math.round(data.main.temp); + const description = data.weather[0].description; + const sunrise = convertUnixToTime(data.sys.sunrise); + const sunset = convertUnixToTime(data.sys.sunset); + const currentTime = new Date().getTime() / 1000; + const isDaytime = currentTime >= data.sys.sunrise && currentTime < data.sys.sunset; + + // Set background image based on weather description and time of day + setBackground(description.toLowerCase(), isDaytime); + + // Data for current weather + currentWeatherElement.innerHTML = ` +
+

${temperature}°C

+

${location}

+

Time: ${convertUnixToTime(data.dt)}

+

${description}

+
+

sunrise ${sunrise}

+

sunset ${sunset}

+
+
+ `; + }) + .catch(error => { + console.error('Error fetching current weather:', error); + alert('Failed to retrieve current weather data. Please try again later.'); + }); +} + +// Fetch forecast data +function fetchForecastData(city) { + fetch(`${FORECAST_URL}?q=${city}&units=metric&APPID=${API_KEY}`) + .then(response => response.json()) + .then(data => { + console.log('Forecast Data:', data); + + const forecastList = data.list; + + const forecastByDay = {}; + + forecastList.forEach(entry => { + const dateTime = entry.dt_txt; + const time = dateTime.split(' ')[1]; + + if (time === '12:00:00') { + const date = dateTime.split(' ')[0]; + if (!forecastByDay[date]) { + forecastByDay[date] = entry; + } + } + }); + + // Data for the next 4 days + const forecastDays = Object.keys(forecastByDay).slice(0, 4); + if (forecastDays.length === 0) { + forecastElement.innerHTML = '

No forecast data available at 12:00 PM.

'; + } else { + forecastElement.innerHTML = ''; + forecastDays.forEach(date => { + const forecast = forecastByDay[date]; + const temperature = Math.round(forecast.main.temp); + const windSpeed = forecast.wind.speed; + + const dayOfWeek = new Date(date).getDay(); + const dayName = weekdays[dayOfWeek]; + + forecastElement.innerHTML += ` +
+

${dayName}

+

${temperature}°C

+

${windSpeed} m/s

+
+ `; + }); + } + }) + .catch(error => { + console.error('Error fetching forecast:', error); + alert('Failed to retrieve forecast data. Please try again later.'); + }); +} + +// Invoking the functions +fetchWeatherData(city); +fetchForecastData(city); diff --git a/daytime-clear.jpg b/daytime-clear.jpg new file mode 100644 index 000000000..ef6e0282d Binary files /dev/null and b/daytime-clear.jpg differ diff --git a/daytime-cloudy.jpg b/daytime-cloudy.jpg new file mode 100644 index 000000000..6fe067e2e Binary files /dev/null and b/daytime-cloudy.jpg differ diff --git a/daytime.jpg b/daytime.jpg new file mode 100644 index 000000000..784059f8a Binary files /dev/null and b/daytime.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 000000000..de0b7be3e --- /dev/null +++ b/index.html @@ -0,0 +1,29 @@ + + + + + + + + + Weather App + + + + + +
+ + +
+ + +
+ + +
+ + + + + \ No newline at end of file diff --git a/night-clear.jpg b/night-clear.jpg new file mode 100644 index 000000000..16028eb52 Binary files /dev/null and b/night-clear.jpg differ diff --git a/night-cloudy.jpg b/night-cloudy.jpg new file mode 100644 index 000000000..15bcbae2b Binary files /dev/null and b/night-cloudy.jpg differ diff --git a/night.jpg b/night.jpg new file mode 100644 index 000000000..c6ee23f47 Binary files /dev/null and b/night.jpg differ diff --git a/style.css b/style.css new file mode 100644 index 000000000..649da38b3 --- /dev/null +++ b/style.css @@ -0,0 +1,231 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +/* Body Styling */ +body { + font-family: Arial, Helvetica, sans-serif; + padding: 20px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + background-color: whitesmoke; +} + +/* Search Bar Styling */ +.search-container { + display: flex; + align-items: center; + position: absolute; + margin-left: 530px; + margin-top: 20px; + top: 10px; + left: 10px; + border-radius: 5px; + z-index: 10; + cursor: pointer; + transition: all 0.3s ease; + width: 40px; + overflow: hidden; +} + +#cityInput { + width: 0; + padding: 0; + font-size: 14px; + outline: none; + opacity: 0; + transition: width 0.3s ease, opacity 0.3s ease; +} + +#searchIcon { + color: white; + font-size: 24px; + cursor: pointer; + transition: transform 0.3s ease; +} + +.search-container.active { + width: 200px; +} + +.search-container.active #cityInput { + width: 150px; + opacity: 1; + padding: 8px; + background-color: rgba(255, 255, 255, 0.5); + border: 1px solid gray; +} + +.search-container.active #searchIcon { + transform: rotate(90deg); +} + +/* Current weather section */ +.currentWeather { + color: white; + padding: 30px; + width: 100%; + max-width: 400px; + position: relative; + overflow: hidden; + background-color: lightblue; + border-bottom-left-radius: 180% 200px; + border-bottom-right-radius: 180% 200px; + margin-bottom: -65px; + z-index: 1; +} + + +/* Temperature */ +.currentWeather .temperature { + font-size: 7rem; + font-weight: bold; + margin-bottom: 10px; + margin-top: 15px; + text-shadow: -1px -1px 0px grey, 1px -1px 0px grey, -1px 1px 0px grey, 1px 1px 0px grey; +} + +/* City Name */ +.currentWeather .location { + font-size: 2.5rem; + font-weight: 200; + margin-bottom: 10px; + text-shadow: -1px -1px 0px grey, 1px -1px 0px grey, -1px 1px 0px grey, 1px 1px 0px grey; +} + +/* Time */ +.currentWeather .time { + font-size: 1.2rem; + margin-bottom: 20px; + text-shadow: -1px -1px 0px grey, 1px -1px 0px grey, -1px 1px 0px grey, 1px 1px 0px grey; +} + +/* Description of weather */ +.currentWeather .description { + font-size: 1.2rem; + font-weight: 200; + margin-bottom: 40px; + text-shadow: -1px -1px 0px grey, 1px -1px 0px grey, -1px 1px 0px grey, 1px 1px 0px grey; +} + +/* Sunrise and Sunset */ +.currentWeather .sun-info { + display: flex; + justify-content: space-around; + font-size: 1.2rem; + color: white; + margin-bottom: 20px; + text-shadow: -1px -1px 0px grey, 1px -1px 0px grey, -1px 1px 0px grey, 1px 1px 0px grey; +} + +/* Forecast Section */ +#forecast { + background-color: rgb(244, 235, 235); + padding: 20px; + width: 100%; + max-width: 400px; + text-align: left; + z-index: 0; + position: relative; +} + +/* Forecast items */ +#forecast div { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0; + margin-top: 20px; +} + +#forecast div:last-child { + border-bottom: none; + margin-bottom: 40px; +} + +#forecast div:first-child { + margin-top: 70px; +} + +/* Day and Weather in forecast */ +#forecast .day { + font-weight: bold; + font-size: 1.1rem; + width: 100px; +} + +#forecast .temp { + font-size: 1.1rem; +} + +#forecast .wind { + font-size: 0.9rem; + color: #666; +} + + + +/* Responsive Design for Smartphones */ +@media screen and (max-width: 480px) { + body { + padding: 5px; + } + + .currentWeather { + padding: 15px; + max-width: 100%; + } + + #forecast { + padding: 10px; + max-width: 100%; + } + + .search-container { + margin-left: 10px; + margin-top: 5px; + } + + .currentWeather .temperature { + font-size: 5rem; + } + + .currentWeather .location { + font-size: 1.5rem; + } + + .currentWeather .description, + .currentWeather .time, + .currentWeather .sun-info { + font-size: 1rem; + } + + #forecast .day { + font-size: 1rem; + } + + #forecast .temp { + font-size: 1rem; + } + + #forecast .wind { + font-size: 0.8rem; + } +} + +@media screen and (min-width: 481px) and (max-width: 1118px) { + + /* Responsive design for ipad air */ + .search-container { + margin-left: 210px; + margin-top: 230px; + margin-bottom: 20px; + width: 40px; + } + +} \ No newline at end of file