diff --git a/README.md b/README.md new file mode 100644 index 0000000..c895a8a --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +## *Weather App* + + Project Screenshot + +### 🔗 *Demo Site* + - Check out the live demo of this project: [Demo Site](https://Jomonh.github.io/Weather-App) + +### *Key Features* + - Responsive + - Clean UI + - Location based weather + - search by city + +### *Technologies Used* + - HTML + - CSS + - JavaScript + - Api used : OpenWeatherMap.org + +## Getting Started + +To get started with this project, you have two options: + +### 1. *Clone the Repository* + - Use the following link to clone the repository to your local machine: + + ```bash + git clone https://github.com/Jomonh/Weather-App.git + + + - Once cloned, navigate to the project directory: + + ```bash + cd Weather-App + + +### 2. *Fork the Repository* + - If you prefer, you can directly *fork* this repository. + - Click the *Fork* button at the top-right of this page. + - After forking, clone the repository from your account: + + ```bash + git clone https://github.com/your-username/your-forked-repository.git + + +### 🌟 *Don’t Forget to Star!* + - If you find this project useful, please consider giving it a star ⭐.It helps others discover it too! + +### *License* + - This project is licensed under the MIT License - see the [LICENSE](https://opensource.org/license/MIT) file for details. diff --git a/assets/cloud.png b/assets/cloud.png new file mode 100644 index 0000000..a1a58a3 Binary files /dev/null and b/assets/cloud.png differ diff --git a/assets/humidity.png b/assets/humidity.png new file mode 100644 index 0000000..a5f26c3 Binary files /dev/null and b/assets/humidity.png differ diff --git a/assets/loading.gif b/assets/loading.gif new file mode 100644 index 0000000..c28b898 Binary files /dev/null and b/assets/loading.gif differ diff --git a/assets/location.png b/assets/location.png new file mode 100644 index 0000000..2595d6f Binary files /dev/null and b/assets/location.png differ diff --git a/assets/preview.png b/assets/preview.png new file mode 100644 index 0000000..444c09d Binary files /dev/null and b/assets/preview.png differ diff --git a/assets/search.png b/assets/search.png new file mode 100644 index 0000000..7f9928a Binary files /dev/null and b/assets/search.png differ diff --git a/assets/wind.png b/assets/wind.png new file mode 100644 index 0000000..f931b28 Binary files /dev/null and b/assets/wind.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..5bbad68 --- /dev/null +++ b/index.html @@ -0,0 +1,104 @@ + + + + + + + weather app + + + + + + + +
+ +

Weather App

+ +
+

Your Weather

+

Search Weather

+
+ +
+ + +
+ +

Grant Location Access

+

Allow Access to get weather Information

+ +
+ + +
+ + +
+ + +
+ +

Loading

+
+ + + + + + +
+ + +
+ + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..d84a4d4 --- /dev/null +++ b/script.js @@ -0,0 +1,170 @@ +const userTab = document.querySelector("[data-userWeather]"); +const searchTab = document.querySelector("[data-searchWeather]"); +const userContainer = document.querySelector(".weather-container"); + +const grantAccessContainer = document.querySelector(".grant-location-container"); +const searchForm = document.querySelector("[data-searchForm]"); +const loadingScreen = document.querySelector(".loading-container"); +const userInfoContainer = document.querySelector(".user-info-container"); + +//initially vairables need???? + +let oldTab = userTab; +const API_KEY = "720818d89500ff08044dcb0a47d5e6ef"; +oldTab.classList.add("current-tab"); +getfromSessionStorage(); + +function switchTab(newTab) { + if(newTab != oldTab) { + oldTab.classList.remove("current-tab"); + oldTab = newTab; + oldTab.classList.add("current-tab"); + + if(!searchForm.classList.contains("active")) { + //kya search form wala container is invisible, if yes then make it visible + userInfoContainer.classList.remove("active"); + grantAccessContainer.classList.remove("active"); + searchForm.classList.add("active"); + } + else { + //main pehle search wale tab pr tha, ab your weather tab visible karna h + searchForm.classList.remove("active"); + userInfoContainer.classList.remove("active"); + //ab main your weather tab me aagya hu, toh weather bhi display karna poadega, so let's check local storage first + //for coordinates, if we haved saved them there. + getfromSessionStorage(); + } + } +} + +userTab.addEventListener("click", () => { + //pass clicked tab as input paramter + switchTab(userTab); +}); + +searchTab.addEventListener("click", () => { + //pass clicked tab as input paramter + switchTab(searchTab); +}); + +//check if cordinates are already present in session storage +function getfromSessionStorage() { + const localCoordinates = sessionStorage.getItem("user-coordinates"); + if(!localCoordinates) { + //agar local coordinates nahi mile + grantAccessContainer.classList.add("active"); + } + else { + const coordinates = JSON.parse(localCoordinates); + fetchUserWeatherInfo(coordinates); + } + +} + +async function fetchUserWeatherInfo(coordinates) { + const {lat, lon} = coordinates; + // make grantcontainer invisible + grantAccessContainer.classList.remove("active"); + //make loader visible + loadingScreen.classList.add("active"); + + //API CALL + try { + const response = await fetch( + `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric` + ); + const data = await response.json(); + + loadingScreen.classList.remove("active"); + userInfoContainer.classList.add("active"); + renderWeatherInfo(data); + } + catch(err) { + loadingScreen.classList.remove("active"); + //HW + + } + +} + +function renderWeatherInfo(weatherInfo) { + //fistly, we have to fethc the elements + + const cityName = document.querySelector("[data-cityName]"); + const countryIcon = document.querySelector("[data-countryIcon]"); + const desc = document.querySelector("[data-weatherDesc]"); + const weatherIcon = document.querySelector("[data-weatherIcon]"); + const temp = document.querySelector("[data-temp]"); + const windspeed = document.querySelector("[data-windspeed]"); + const humidity = document.querySelector("[data-humidity]"); + const cloudiness = document.querySelector("[data-cloudiness]"); + + console.log(weatherInfo); + + //fetch values from weatherINfo object and put it UI elements + cityName.innerText = weatherInfo?.name; + countryIcon.src = `https://flagcdn.com/144x108/${weatherInfo?.sys?.country.toLowerCase()}.png`; + desc.innerText = weatherInfo?.weather?.[0]?.description; + weatherIcon.src = `http://openweathermap.org/img/w/${weatherInfo?.weather?.[0]?.icon}.png`; + temp.innerText = `${weatherInfo?.main?.temp} °C`; + windspeed.innerText = `${weatherInfo?.wind?.speed} m/s`; + humidity.innerText = `${weatherInfo?.main?.humidity}%`; + cloudiness.innerText = `${weatherInfo?.clouds?.all}%`; + + +} + +function getLocation() { + if(navigator.geolocation) { + navigator.geolocation.getCurrentPosition(showPosition); + } + else { + //HW - show an alert for no gelolocation support available + } +} + +function showPosition(position) { + + const userCoordinates = { + lat: position.coords.latitude, + lon: position.coords.longitude, + } + + sessionStorage.setItem("user-coordinates", JSON.stringify(userCoordinates)); + fetchUserWeatherInfo(userCoordinates); + +} + +const grantAccessButton = document.querySelector("[data-grantAccess]"); +grantAccessButton.addEventListener("click", getLocation); + +const searchInput = document.querySelector("[data-searchInput]"); + +searchForm.addEventListener("submit", (e) => { + e.preventDefault(); + let cityName = searchInput.value; + + if(cityName === "") + return; + else + fetchSearchWeatherInfo(cityName); +}) + +async function fetchSearchWeatherInfo(city) { + loadingScreen.classList.add("active"); + userInfoContainer.classList.remove("active"); + grantAccessContainer.classList.remove("active"); + + try { + const response = await fetch( + `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric` + ); + const data = await response.json(); + loadingScreen.classList.remove("active"); + userInfoContainer.classList.add("active"); + renderWeatherInfo(data); + } + catch(err) { + //hW + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..81cdad6 --- /dev/null +++ b/style.css @@ -0,0 +1,243 @@ +*,*::before,*::after { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Merriweather Sans', sans-serif; +} + +.wrapper{ + width:100vw; + height:100vh; + color: #ffffff; + background-image: linear-gradient(160deg, #110b21 0%, #6c17da 100%);; +} + +h1 { + text-align: center; + text-transform:uppercase; + padding-top: 20px; +} + +.tab-container { + width:90%; + max-width: 550px; + margin: 0 auto; + margin-top: 4rem; + display: flex; + justify-content: space-between; +} + +.tab{ + cursor: pointer; + font-size: 0.875rem; + letter-spacing: 1.75px; + padding: 5px 8px; +} + +.tab.current-tab{ + background-color: rgba(0, 0, 0, 0.463); + border-radius: 5px; + padding: 10px; +} + +.weather-container{ + margin-block:4rem; +} + +.btn{ + all: unset; + font-size: 0.85rem; + text-transform: uppercase; + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.292); + cursor: pointer; + padding: 10px 30px; +} +.btn:hover{ + background-color: rgba(0, 0, 0, 0.463); +} + +.sub-container{ + display:flex; + flex-direction:column; + align-items: center; +} + +.grant-location-container{ + display:none; +} + +.grant-location-container.active{ + display:flex; +} + +.grant-location-container img{ + margin-bottom: 2rem; +} + +.grant-location-container p:first-of-type{ + font-size: 1.75rem; + font-weight: 600; +} + +.grant-location-container p:last-of-type{ + font-size:0.85rem; + font-weight: 500; + margin-top: 0.75rem; + margin-bottom: 1.75rem; + letter-spacing: 0.75px; +} + +.loading-container{ + display: none; +} + +.loading-container.active{ + display: flex; +} + +.loading-container p{ + text-transform: uppercase; +} + +.user-info-container{ + display:none; +} + +.user-info-container.active{ + display: flex; +} + +.name{ + display: flex; + align-items: center; + gap: 0 0.5rem; + margin-bottom: 1rem; +} + +.user-info-container p{ + font-size:1.5rem; + font-weight:200; +} + +.user-info-container img{ + width:60px; + height:60px; +} + +.name p{ + font-size:2rem; +} + +.name img{ + width: 30px; + height:30px; + object-fit: contain; +} + +.user-info-container p[data-temp] { + font-size:2.75rem; + font-weight:700; +} + +.parameter-container{ + width:90%; + display: flex; + gap: 10px 20px; + justify-content: center; + align-items: center; + margin-top: 1rem; +} + +.parameter{ + width:30%; + max-width:200px; + background-color: rgba(0, 0, 0, 0.463); + border-radius: 5px; + padding:1rem; + display: flex; + flex-direction: column; + gap:5px 0; + align-items: center; + +} + +.parameter img{ + width:50px; + height:50px; +} + +.parameter p:first-of-type{ + font-size: 1.5rem; + font-weight:600; + text-transform: uppercase; +} + +.parameter p:last-of-type{ + font-size: 1rem; + font-weight: 200; +} + +.form-container{ + display: none; + width:90%; + max-width:550px; + margin:0 auto; + justify-content: center; + align-items: center; + gap: 0 10px; + margin-bottom: 3rem; +} + +.form-container.active{ + display: flex; +} + +.form-container input{ + all:unset; + width: calc(100% - 80px); + height:40px; + padding: 0px 20px 3px; + background-color: rgba(0, 0, 0, 0.292); + border-radius: 25px; +} + +.form-container input::placeholder{ + color: #ffffffc1; +} + +.form-container input:focus{ + outline: 2px solid #ffffffc1; +} + +.form-container .btn { + padding:unset; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 100%; +} + +@media screen and (max-width:600px) { + + .parameter{ + height: 120px; + } + + .parameter img{ + width:40px; + height:40px; + } + + .parameter p:first-of-type{ + font-size: 15px; + } + + .parameter p:last-of-type{ + font-size: 12px; + font-weight: 300; + } + +} \ No newline at end of file