├── LICENSE ├── README.md ├── css ├── palette.css └── style.css ├── icons ├── Preview.png ├── black result.png ├── clear.svg ├── cloud.svg ├── favicon.svg ├── haze.svg ├── icon1.png ├── icon2.png ├── icon3.png ├── pink result.png ├── rain.svg ├── readme icon.png ├── snow.svg ├── storm.svg ├── theme.png └── weather app.png ├── index.html └── js └── script.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ashutosh Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![GitHub repo size](https://img.shields.io/github/repo-size/codeaashu/Weather-State) 4 | ![GitHub stars](https://img.shields.io/github/stars/codeaashu/Weather-State?style=social) 5 | ![GitHub forks](https://img.shields.io/github/forks/codeaashu/Weather-State?style=social) 6 | [![Twitter Follow](https://img.shields.io/twitter/follow/warrior_aashuu?style=social)](https://twitter.com/intent/follow?screen_name=warrior_aashuu)
7 | 8 | 9 |

Weather State

10 | 11 | `Weather Web App to get the current weather state using the openweathermap API 🌐` 12 | 13 | ➥ Live Demo 14 | 15 |
16 | 17 | 18 | 19 |
20 | 21 | `Don't forget to starred this repository ⭐` 22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /css/palette.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/css/palette.css -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* Import Google Font - Poppins */ 2 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"); 3 | 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | font-family: "Poppins", sans-serif; 9 | } 10 | 11 | :root { 12 | --primary-color: #495af7; 13 | --secondary-color: #fff; 14 | --text-color: #fff; 15 | --input-color: #000; 16 | --light-grey: #f5f5f5; 17 | --pending-color: #b7e4ec; 18 | --pending-text-color: #0c6047; 19 | --error-color: #ffc1c5; 20 | --error-text-color: #631920; 21 | } 22 | 23 | body { 24 | display: flex; 25 | align-items: center; 26 | justify-content: center; 27 | min-height: 100vh; 28 | background: var(--primary-color); 29 | color: var(--text-color); 30 | } 31 | 32 | ::selection { 33 | color: #fff; 34 | background: var(--primary-color); 35 | } 36 | 37 | .wrapper { 38 | width: 90%; 39 | background: #fff; 40 | border-radius: 7px; 41 | background: rgba(255, 255, 255, 0.1); 42 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 43 | backdrop-filter: blur(3px); 44 | -webkit-backdrop-filter: blur(3px); 45 | border: 1px solid rgba(255, 255, 255, 0.18); 46 | margin: 30px; 47 | } 48 | 49 | .wrapper header { 50 | display: flex; 51 | font-size: 21px; 52 | font-weight: 500; 53 | color: var(--text-color); 54 | padding: 16px 15px; 55 | align-items: center; 56 | border-bottom: 1px solid #ccc; 57 | } 58 | 59 | header i { 60 | font-size: 0em; 61 | cursor: pointer; 62 | margin-right: 8px; 63 | } 64 | 65 | .wrapper.active header i { 66 | margin-left: 5px; 67 | font-size: 30px; 68 | } 69 | 70 | .wrapper .input-part { 71 | margin: 20px 25px 30px; 72 | } 73 | 74 | .container{ 75 | display: flex; 76 | justify-content: center; 77 | align-items: center; 78 | } 79 | 80 | .wrapper.active .input-part { 81 | display: none; 82 | } 83 | 84 | .input-part .info-txt { 85 | display: none; 86 | font-size: 17px; 87 | text-align: center; 88 | padding: 12px 10px; 89 | border-radius: 7px; 90 | margin-bottom: 15px; 91 | } 92 | 93 | .input-part .info-txt.error { 94 | color: var(--error-text-color); 95 | display: block; 96 | background: var(--error-color); 97 | border: 1px solid #f5c6cb; 98 | } 99 | 100 | .input-part .info-txt.pending { 101 | color: var(--pending-text-color); 102 | display: block; 103 | background: var(--pending-color); 104 | border: 1px solid #bee5eb; 105 | } 106 | 107 | .input-part :where(input, button) { 108 | width: 100%; 109 | height: 55px; 110 | border: none; 111 | outline: none; 112 | font-size: 18px; 113 | border-radius: 7px; 114 | } 115 | 116 | .input-part input { 117 | text-align: center; 118 | padding: 0 15px; 119 | border: 1px solid #ccc; 120 | background-color: #fff; 121 | } 122 | 123 | .input-part input:is(:focus, :valid) { 124 | border: 2px solid var(--primary-color); 125 | } 126 | 127 | .input-part input::placeholder { 128 | color: rgb(24, 24, 24) !important; 129 | } 130 | 131 | .input-part .separator { 132 | height: 1px; 133 | width: 100%; 134 | margin: 25px 0; 135 | position: relative; 136 | display: flex; 137 | align-items: center; 138 | justify-content: center; 139 | } 140 | 141 | .separator::before { 142 | content: "or"; 143 | color: var(--light-grey); 144 | font-size: 19px; 145 | padding: 0 15px; 146 | border-radius: 20px; 147 | } 148 | 149 | .input-part button { 150 | color: var(--text-color); 151 | cursor: pointer; 152 | background: var(--primary-color); 153 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 154 | transition: 0.3s ease; 155 | } 156 | 157 | .input-part button:hover { 158 | transform: scale(1.03); 159 | } 160 | 161 | .wrapper .weather-part { 162 | display: none; 163 | margin: 30px 0 0; 164 | align-items: center; 165 | justify-content: center; 166 | flex-direction: column; 167 | } 168 | 169 | .wrapper.active .weather-part { 170 | display: flex; 171 | } 172 | 173 | .weather-part img { 174 | max-width: 125px; 175 | } 176 | 177 | .weather-part .temp { 178 | display: flex; 179 | font-weight: 500; 180 | font-size: 72px; 181 | } 182 | 183 | .weather-part .temp .numb { 184 | font-weight: 600; 185 | } 186 | 187 | .weather-part .temp .deg { 188 | font-size: 40px; 189 | display: block; 190 | margin: 10px 5px 0 0; 191 | } 192 | 193 | .weather-part .weather { 194 | font-size: 21px; 195 | text-align: center; 196 | margin: -5px 20px 15px; 197 | } 198 | 199 | .weather-part .location { 200 | display: flex; 201 | font-size: 19px; 202 | padding: 0 20px; 203 | text-align: center; 204 | margin-bottom: 30px; 205 | align-items: flex-start; 206 | } 207 | 208 | .location i { 209 | font-size: 22px; 210 | margin: 4px 5px 0 0; 211 | } 212 | 213 | .weather-part .bottom-details { 214 | display: flex; 215 | width: 100%; 216 | justify-content: space-between; 217 | border-top: 1px solid #ccc; 218 | border-bottom: 1px solid #ccc; 219 | } 220 | 221 | .bottom-details .column { 222 | display: flex; 223 | width: 100%; 224 | padding: 15px 0; 225 | align-items: center; 226 | justify-content: center; 227 | } 228 | 229 | .column i { 230 | color: var(--secondary-color); 231 | font-size: 40px; 232 | } 233 | 234 | .column.humidity { 235 | border-left: 1px solid #ccc; 236 | } 237 | 238 | .column.wind { 239 | border-left: 1px solid #ccc; 240 | } 241 | 242 | .column.date-time { 243 | border-left: 1px solid #ccc; 244 | } 245 | 246 | .column .details { 247 | margin-left: 3px; 248 | } 249 | 250 | .details .temp, 251 | .humidity span { 252 | font-size: 18px; 253 | font-weight: 500; 254 | margin-top: -3px; 255 | } 256 | 257 | .details .temp .deg { 258 | margin: 0; 259 | font-size: 17px; 260 | padding: 0 2px 0 1px; 261 | } 262 | 263 | .column .details p { 264 | font-size: 14px; 265 | margin-top: -6px; 266 | } 267 | 268 | .humidity i { 269 | font-size: 37px; 270 | } 271 | 272 | /* COLOR PALETTE */ 273 | .color-palette { 274 | position: absolute; 275 | display: flex; 276 | flex-direction: column; 277 | justify-content: center; 278 | margin: 12px; 279 | right: 0; 280 | background: rgba(255, 255, 255, 0.2); 281 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 282 | backdrop-filter: blur(1.5px); 283 | -webkit-backdrop-filter: blur(1.5px); 284 | border-radius: 10px; 285 | border: 1px solid rgba(255, 255, 255, 0.18); 286 | border-radius: 30px; 287 | padding: 8px; 288 | text-align: center; 289 | } 290 | 291 | .color-palette .theme-color { 292 | background-color: red; 293 | border-radius: 50%; 294 | width: 30px; 295 | height: 30px; 296 | cursor: pointer; 297 | } 298 | 299 | .color-palette .theme-color:not(:last-child) { 300 | margin-bottom: 10px; 301 | } 302 | 303 | /* DARK MODE */ 304 | .dark-mode { 305 | position: absolute; 306 | top: 12px; 307 | right: 12px; 308 | background: rgba(255, 255, 255, 0.25); 309 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 310 | backdrop-filter: blur(4px); 311 | -webkit-backdrop-filter: blur(4px); 312 | border-radius: 50%; 313 | padding: 8px 13px; 314 | border: 1px solid rgba(255, 255, 255, 0.18); 315 | } 316 | 317 | .dark-mode span { 318 | font-size: 20px; 319 | cursor: pointer; 320 | } 321 | 322 | /* COPYRIGHT */ 323 | 324 | .copyright { 325 | position: absolute; 326 | top: 0; 327 | left: 0; 328 | right: 0; 329 | display: flex; 330 | justify-content: center; 331 | align-items: center; 332 | padding: var(--main-padding); 333 | background: var(--bg-color); 334 | color: var(--text-color); 335 | font-size: 14px; 336 | font-weight: 300; 337 | letter-spacing: 1px; 338 | text-transform: uppercase; 339 | margin-top: 20px; 340 | text-align: center; 341 | } 342 | 343 | .copyright p a { 344 | color: var(--text-color); 345 | text-decoration: none; 346 | font-weight: 500; 347 | } 348 | 349 | /* Media Queries */ 350 | @media only screen and (max-width: 436px) { 351 | .wrapper { 352 | max-width: 90%; 353 | min-width: 90%; 354 | } 355 | 356 | .color-palette { 357 | max-width: 90%; 358 | min-width: 90%; 359 | } 360 | 361 | .color-palette .theme-color { 362 | width: 22px; 363 | height: 22px; 364 | } 365 | } 366 | 367 | @media only screen and (max-width: 636px) { 368 | .color-palette { 369 | display: flex; 370 | flex-direction: row; 371 | justify-content: center; 372 | left: 50%; 373 | transform: translateX(-50%); 374 | bottom: 0; 375 | padding: 0; 376 | margin: 0; 377 | width: 400px; 378 | margin-bottom: 16px; 379 | } 380 | 381 | .color-palette .theme-color { 382 | margin-top: 10px; 383 | } 384 | 385 | .color-palette .theme-color:not(:last-child) { 386 | margin-right: 10px; 387 | } 388 | } 389 | 390 | .forecast-details { 391 | display: flex; 392 | flex-wrap: wrap; /* Ensure items wrap to the next line when necessary */ 393 | justify-content: space-around; 394 | padding: 10px; /* Adjust padding as needed */ 395 | } 396 | 397 | .forecast-card { 398 | display: flex; 399 | flex-direction: column; 400 | align-items: center; 401 | justify-content: center; 402 | padding: 20px; 403 | background: rgba(255, 255, 255, 0.1); 404 | border-radius: 10px; 405 | margin-bottom: 10px; 406 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 407 | backdrop-filter: blur(3px); 408 | -webkit-backdrop-filter: blur(3px); 409 | border: 1px solid rgba(255, 255, 255, 0.18); 410 | } 411 | 412 | .forecast-card .forecast-day { 413 | font-size: 21px; 414 | font-weight: 500; 415 | margin-bottom: 10px; 416 | } 417 | 418 | .forecast-card img { 419 | max-width: 80px; 420 | } 421 | 422 | .forecast-card .forecast-temp { 423 | display: flex; 424 | align-items: center; 425 | justify-content: center; 426 | margin: 10px 0; 427 | } 428 | 429 | .forecast-card .forecast-temp .max-temp, 430 | .forecast-card .forecast-temp .min-temp { 431 | font-size: 18px; 432 | font-weight: 500; 433 | margin: 0 5px; 434 | } 435 | 436 | .forecast-card .forecast-desc { 437 | font-size: 16px; 438 | text-align: center; 439 | } -------------------------------------------------------------------------------- /icons/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/Preview.png -------------------------------------------------------------------------------- /icons/black result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/black result.png -------------------------------------------------------------------------------- /icons/clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /icons/cloud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /icons/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /icons/haze.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /icons/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/icon1.png -------------------------------------------------------------------------------- /icons/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/icon2.png -------------------------------------------------------------------------------- /icons/icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/icon3.png -------------------------------------------------------------------------------- /icons/pink result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/pink result.png -------------------------------------------------------------------------------- /icons/rain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 19 | 20 | 24 | 27 | 28 | 29 | 32 | 35 | 38 | 41 | 42 | 43 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /icons/readme icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/readme icon.png -------------------------------------------------------------------------------- /icons/snow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /icons/storm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /icons/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/theme.png -------------------------------------------------------------------------------- /icons/weather app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeaashu/Weather-State/4b4d0a0841b901f45c758b74414f067e3113815c/icons/weather app.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Weather State 12 | 13 | 14 | 15 |
16 |
17 |
18 |
Weather App
19 |
20 |

21 |
22 | 28 |
29 | 30 |
31 |
32 |
33 | Weather Icon 34 |
35 | _ 36 | °C 37 |
38 |
_ _
39 |
40 | 41 | _, _ 42 |
43 |
44 |
45 | 46 |
47 |
48 | _ 49 | °C 50 |
51 |

Feels like

52 |
53 |
54 |
55 | 56 |
57 | _ 58 |

Humidity

59 |
60 |
61 |
62 | 63 |
64 | _ 65 |

Wind Speed

66 |
67 |
68 |
69 | 70 |
71 | _ 72 |

Date & Time

73 |
74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 90 |
91 | 92 |
93 | 94 | 95 | 96 |
97 | 98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
109 | 114 | 115 | 116 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /js/script.js: -------------------------------------------------------------------------------- 1 | const wrapper = document.querySelector(".wrapper"), 2 | inputPart = document.querySelector(".input-part"), 3 | infoTxt = inputPart.querySelector(".info-txt"), 4 | inputField = inputPart.querySelector("input"), 5 | locationBtn = inputPart.querySelector("button"), 6 | weatherPart = wrapper.querySelector(".weather-part"), 7 | forecastSection = wrapper.querySelector(".forecast"), 8 | forecastDetails = forecastSection.querySelector(".forecast-details"), 9 | wIcon = weatherPart.querySelector("img"), 10 | arrowBack = wrapper.querySelector("header i"); 11 | 12 | let api; 13 | let apiKey = "b190a0605344cc4f3af08d0dd473dd25"; 14 | 15 | const weatherChartCtx = document.getElementById("weatherChart").getContext("2d"); 16 | let weatherChart; 17 | 18 | // Function to create or update weather chart 19 | function createWeatherChart(labels, data) { 20 | if (weatherChart) { 21 | weatherChart.destroy(); 22 | } 23 | 24 | weatherChart = new Chart(weatherChartCtx, { 25 | type: "line", // Change chart type as needed (line, bar, etc.) 26 | data: { 27 | labels: labels, 28 | datasets: [{ 29 | label: "Temperature (°C)", 30 | data: data, 31 | backgroundColor: "rgba(54, 162, 235, 0.6)", 32 | borderColor: "rgba(54, 162, 235, 1)", 33 | borderWidth: 1, 34 | fill: false 35 | }] 36 | }, 37 | options: { 38 | responsive: true, 39 | maintainAspectRatio: false, 40 | scales: { 41 | yAxes: [{ 42 | ticks: { 43 | beginAtZero: true 44 | } 45 | }] 46 | } 47 | } 48 | }); 49 | } 50 | 51 | // Event listeners 52 | inputField.addEventListener("keyup", (e) => { 53 | if (e.key === "Enter" && inputField.value.trim() !== "") { 54 | requestApi(inputField.value.trim()); 55 | } 56 | }); 57 | 58 | locationBtn.addEventListener("click", () => { 59 | if (navigator.geolocation) { 60 | navigator.geolocation.getCurrentPosition(onSuccess, onError); 61 | } else { 62 | alert("Your browser does not support geolocation API."); 63 | } 64 | }); 65 | 66 | // Function to request weather data 67 | function requestApi(city) { 68 | api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`; 69 | fetchData(); 70 | } 71 | 72 | // Function to handle geolocation success 73 | function onSuccess(position) { 74 | const { latitude, longitude } = position.coords; 75 | api = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=metric&appid=${apiKey}`; 76 | fetchData(); 77 | } 78 | 79 | // Function to handle geolocation error 80 | function onError(error) { 81 | infoTxt.innerText = error.message; 82 | infoTxt.classList.add("error"); 83 | clearWeatherData(); 84 | } 85 | 86 | // Function to fetch weather data from API 87 | function fetchData() { 88 | infoTxt.innerText = "Fetching weather details..."; 89 | infoTxt.classList.add("pending"); 90 | 91 | fetch(api) 92 | .then((res) => res.json()) 93 | .then((result) => { 94 | if (result.cod && result.cod === "404") { 95 | infoTxt.innerText = `${inputField.value} is not a valid city name`; 96 | infoTxt.classList.replace("pending", "error"); 97 | clearWeatherData(); 98 | } else { 99 | clearWeatherData(); // Clear previous weather and forecast data 100 | weatherDetails(result); 101 | fetchForecast(result.coord.lat, result.coord.lon); // Fetch 7-day forecast 102 | fetchHourlyForecast(result.coord.lat, result.coord.lon); // Fetch hourly forecast 103 | } 104 | }) 105 | .catch(() => { 106 | infoTxt.innerText = "Something went wrong"; 107 | infoTxt.classList.replace("pending", "error"); 108 | clearWeatherData(); 109 | }); 110 | } 111 | 112 | 113 | 114 | // Function to display weather details 115 | function weatherDetails(info) { 116 | const { name: city, sys: { country }, weather: [{ description, id }], main: { temp, feels_like, humidity }, wind: { speed }, dt } = info; 117 | 118 | const weatherDate = new Date(dt * 1000).toLocaleString('en', { 119 | weekday: 'long', 120 | year: 'numeric', 121 | month: 'long', 122 | day: 'numeric', 123 | hour: 'numeric', 124 | minute: 'numeric', 125 | hour12: true 126 | }); 127 | 128 | wIcon.src = getWeatherIcon(id); 129 | weatherPart.querySelector(".temp .numb").innerText = Math.round(temp); 130 | weatherPart.querySelector(".weather").innerText = description; 131 | weatherPart.querySelector(".location span").innerText = `${city}, ${country}`; 132 | weatherPart.querySelector(".temp .numb-2").innerText = Math.round(feels_like); 133 | weatherPart.querySelector(".humidity span").innerText = `${humidity}%`; 134 | weatherPart.querySelector(".wind span").innerText = `${speed} m/s`; 135 | weatherPart.querySelector(".date-time").innerText = weatherDate; 136 | 137 | infoTxt.classList.remove("pending", "error"); 138 | infoTxt.innerText = ""; 139 | inputField.value = ""; 140 | wrapper.classList.add("active"); 141 | } 142 | 143 | // Function to fetch daily forecast data 144 | function fetchForecast(latitude, longitude) { 145 | const forecastApi = `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&exclude=current,minutely,hourly&units=metric&appid=${apiKey}`; 146 | 147 | fetch(forecastApi) 148 | .then((res) => res.json()) 149 | .then((data) => { 150 | if (data.cod && data.cod === "404") { 151 | infoTxt.innerText = "Forecast data not available"; 152 | infoTxt.classList.replace("pending", "error"); 153 | clearForecast(); 154 | } else { 155 | updateForecast(data.daily.slice(1, 8)); // Update forecast for next 7 days 156 | } 157 | }) 158 | .catch(() => { 159 | infoTxt.innerText = "Forecast data not available"; 160 | infoTxt.classList.replace("pending", "error"); 161 | clearForecast(); 162 | }); 163 | } 164 | 165 | 166 | // Function to fetch hourly forecast data 167 | function fetchHourlyForecast(latitude, longitude) { 168 | const hourlyForecastApi = `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&exclude=current,daily,minutely&units=metric&appid=${apiKey}`; 169 | 170 | fetch(hourlyForecastApi) 171 | .then((res) => res.json()) 172 | .then((data) => { 173 | if (data.cod && data.cod === "404") { 174 | infoTxt.innerText = "Hourly forecast data not available"; 175 | infoTxt.classList.replace("pending", "error"); 176 | clearHourlyForecast(); 177 | } else { 178 | updateHourlyForecast(data.hourly.slice(0, 24)); // Update hourly forecast for next 24 hours 179 | } 180 | }) 181 | .catch(() => { 182 | infoTxt.innerText = "Hourly forecast data not available"; 183 | infoTxt.classList.replace("pending", "error"); 184 | clearHourlyForecast(); 185 | }); 186 | } 187 | 188 | // Function to update daily forecast 189 | function updateForecast(dailyData) { 190 | forecastDetails.innerHTML = ""; // Clear previous forecast details 191 | 192 | dailyData.forEach((day) => { 193 | const { dt, weather: [{ description, id }], temp: { max, min } } = day; 194 | const dayOfWeek = new Date(dt * 1000).toLocaleDateString('en', { weekday: 'long' }); 195 | 196 | const forecastCard = document.createElement("div"); 197 | forecastCard.classList.add("forecast-card"); 198 | forecastCard.innerHTML = ` 199 |
${dayOfWeek}
200 | Weather Icon 201 |
202 | ${Math.round(max)}°C / 203 | ${Math.round(min)}°C 204 |
205 |
${description}
206 | `; 207 | forecastDetails.appendChild(forecastCard); 208 | }); 209 | } 210 | 211 | // Function to update hourly forecast 212 | function updateHourlyForecast(hourlyData) { 213 | const labels = []; 214 | const data = []; 215 | 216 | hourlyData.forEach((hour) => { 217 | const { dt, temp } = hour; 218 | const hourOfDay = new Date(dt * 1000).toLocaleTimeString('en', { hour: 'numeric', hour12: true }); 219 | labels.push(hourOfDay); 220 | data.push(temp); 221 | }); 222 | 223 | createWeatherChart(labels, data); // Create or update hourly weather chart 224 | } 225 | 226 | // Function to clear weather data 227 | function clearWeatherData() { 228 | wIcon.src = ""; 229 | weatherPart.querySelector(".temp .numb").innerText = ""; 230 | weatherPart.querySelector(".weather").innerText = ""; 231 | weatherPart.querySelector(".location span").innerText = ""; 232 | weatherPart.querySelector(".temp .numb-2").innerText = ""; 233 | weatherPart.querySelector(".humidity span").innerText = ""; 234 | weatherPart.querySelector(".wind span").innerText = ""; 235 | weatherPart.querySelector(".date-time").innerText = ""; 236 | infoTxt.innerText = ""; 237 | forecastSection.style.display = "block"; // Ensure forecast section is visible 238 | clearForecast(); // Clear previous forecast data 239 | clearHourlyForecast(); // Clear previous hourly forecast data 240 | } 241 | 242 | 243 | // Function to clear forecast data 244 | function clearForecast() { 245 | forecastDetails.innerHTML = ""; // Clear daily forecast details 246 | } 247 | 248 | // Function to clear hourly forecast data 249 | function clearHourlyForecast() { 250 | if (weatherChart) { 251 | weatherChart.destroy(); 252 | } 253 | } 254 | 255 | // Function to get weather icon based on weather id 256 | function getWeatherIcon(weatherId) { 257 | if (weatherId === 800) { 258 | return "icons/clear.svg"; 259 | } else if (weatherId >= 200 && weatherId <= 232) { 260 | return "icons/storm.svg"; 261 | } else if (weatherId >= 600 && weatherId <= 622) { 262 | return "icons/snow.svg"; 263 | } else if (weatherId >= 701 && weatherId <= 781) { 264 | return "icons/haze.svg"; 265 | } else if (weatherId >= 801 && weatherId <= 804) { 266 | return "icons/cloud.svg"; 267 | } else if ((weatherId >= 500 && weatherId <= 531) || (weatherId >= 300 && weatherId <= 321)) { 268 | return "icons/rain.svg"; 269 | } else { 270 | return "icons/unknown.svg"; 271 | } 272 | } 273 | 274 | // Event listener for back button 275 | arrowBack.addEventListener("click", () => { 276 | wrapper.classList.remove("active"); 277 | clearWeatherData(); 278 | }); 279 | 280 | // Change Color Theme 281 | var isDark = false; 282 | const colors = [ 283 | "hsl(345, 80%, 50%)", 284 | "hsl(100, 80%, 50%)", 285 | "hsl(200, 80%, 50%)", 286 | "hsl(227, 66%, 55%)", 287 | "hsl(26, 80%, 50%)", 288 | "hsl(44, 90%, 51%)", 289 | "hsl(280, 100%, 65%)", 290 | "hsl(480, 100%, 25%)", 291 | "hsl(180, 100%, 25%)", 292 | ]; 293 | const colorBtns = document.querySelectorAll(".theme-color"); 294 | const darkModeBtn = document.querySelector(".dark-mode-btn"); 295 | 296 | darkModeBtn.addEventListener("click", () => { 297 | isDark = !isDark; 298 | changeTheme(isDark ? "#000" : colors[3]); 299 | }); 300 | 301 | colorBtns.forEach((btn, index) => { 302 | btn.style.backgroundColor = colors[index]; 303 | btn.addEventListener("click", () => { 304 | changeTheme(btn.style.backgroundColor); 305 | }); 306 | }); 307 | 308 | function changeTheme(color) { 309 | document.documentElement.style.setProperty("--primary-color", color); 310 | saveTheme(color); 311 | } 312 | 313 | function saveTheme(color) { 314 | localStorage.setItem("theme", color); 315 | } 316 | 317 | function getTheme() { 318 | const theme = localStorage.getItem("theme"); 319 | if (theme) { 320 | changeTheme(theme); 321 | } 322 | } 323 | 324 | getTheme(); // Initialize theme on page load 325 | --------------------------------------------------------------------------------