├── 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 | 
4 | 
5 | 
6 | [](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 |
19 |
32 |
33 |
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 |
110 |
111 | Weather State By aashuu
112 |
113 |
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 |
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 |
--------------------------------------------------------------------------------