├── weather.jpg
├── README.md
├── index.html
├── style.css
└── script.js
/weather.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LemonSync/Weather_App/HEAD/weather.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ⛅ Weather App 🌦
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 🌍 Aplikasi Cuaca Sederhana dengan Prakiraan 5 Hari 📊
11 |
12 |
13 |
14 | Cek Websitenya Klik Saya
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ---
23 |
24 | ## ✨ Tentang Proyek Ini
25 | **Weather App** adalah aplikasi cuaca berbasis web yang menampilkan informasi cuaca terkini dan prakiraan 5 hari ke depan. Dibangun menggunakan **HTML, CSS, dan JavaScript** dengan integrasi API cuaca.
26 |
27 | 🔹 **Fitur Utama**:
28 | - 🔍 **Cari Kota** – Masukkan nama kota untuk melihat cuaca saat ini
29 | - 📍 **Gunakan Lokasi** – Dapatkan informasi cuaca berdasarkan lokasi langsung
30 | - 🌡 **Detail Cuaca** – Menampilkan suhu, kecepatan angin, dan kelembapan
31 | - 📅 **Prakiraan 5 Hari** – Lihat cuaca dalam beberapa hari ke depan
32 |
33 | ---
34 |
35 |
36 | ## 🛠️ Teknologi yang Digunakan
37 |
38 |
39 |
40 |
41 |
42 |
43 | ---
44 |
45 | ## 📬 Kontak
46 | Jika ada pertanyaan atau ingin berkontribusi, hubungi saya di:
47 | - 🌐 **GitHub**: [LemonSync](https://github.com/LemonSync)
48 | - 📧 **Email**: [grouplemon0@gmail.com](mailto:grouplemon0@gmail.com)
49 | - 🐦 **Twitter**: [@SyncLemon](https://twitter.com/SyncLemon)
50 | - 📞 **WhatsApp**: [Lemon](https://wa.me/6282172175234)
51 |
52 | ---
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Weather App
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Weather Dashboard
21 |
22 |
29 |
30 |
31 |
32 |
_______ ( ㅤㅤㅤㅤㅤ )
33 | Temperature: ㅤㅤㅤ°C
34 | Angin:ㅤㅤM/S
35 | Kelembapan: ㅤ%
36 |
37 |
38 |
39 |
Prakiraan 5-Hari
40 |
41 |
42 | ( ㅤㅤㅤㅤㅤ )
43 | Temp: ㅤㅤ°C
44 | Angin: ㅤㅤM/S
45 | Kelembapan: ㅤ%
46 |
47 |
48 | ( ㅤㅤㅤㅤㅤ )
49 | Temp: ㅤㅤ°C
50 | Angin: ㅤㅤM/S
51 | Kelembapan: ㅤ%
52 |
53 |
54 | ( ㅤㅤㅤㅤㅤ )
55 | Temp: ㅤㅤ°C
56 | Angin: ㅤㅤM/S
57 | Kelembapan: ㅤ%
58 |
59 |
60 | ( ㅤㅤㅤㅤㅤ )
61 | Temp: ㅤㅤ°C
62 | Angin: ㅤㅤM/S
63 | Kelembapan: ㅤ%
64 |
65 |
66 | ( ㅤㅤㅤㅤㅤ )
67 | Temp: ㅤㅤ°C
68 | Angin: ㅤㅤM/S
69 | Kelembapan: ㅤ%
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | /* Import Google font - Open Sans */
2 | @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap');
3 |
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | font-family: 'Open Sans', sans-serif;
9 | }
10 |
11 | body {
12 | background: #E3F2FD;
13 | }
14 |
15 | h1 {
16 | background: #5372F0;
17 | font-size: 1.75rem;
18 | text-align: center;
19 | padding: 18px 0;
20 | color: #fff;
21 | }
22 |
23 | .container {
24 | display: flex;
25 | gap: 35px;
26 | padding: 30px;
27 | }
28 |
29 | .weather-input {
30 | width: 550px;
31 | }
32 |
33 | .weather-input input {
34 | height: 46px;
35 | width: 100%;
36 | outline: none;
37 | font-size: 1.07rem;
38 | padding: 0 17px;
39 | margin: 10px 0 20px 0;
40 | border-radius: 4px;
41 | border: 1px solid #ccc;
42 | }
43 |
44 | .weather-input input:focus {
45 | padding: 0 16px;
46 | border: 2px solid #5372F0;
47 | }
48 |
49 | .weather-input .separator {
50 | height: 1px;
51 | width: 100%;
52 | margin: 25px 0;
53 | background: #BBBBBB;
54 | display: flex;
55 | align-items: center;
56 | justify-content: center;
57 | }
58 |
59 | .weather-input .separator::before {
60 | content: "or";
61 | color: #6C757D;
62 | font-size: 1.18rem;
63 | padding: 0 15px;
64 | margin-top: -4px;
65 | background: #E3F2FD;
66 | }
67 |
68 | .weather-input button {
69 | width: 100%;
70 | padding: 10px 0;
71 | cursor: pointer;
72 | outline: none;
73 | border: none;
74 | border-radius: 4px;
75 | font-size: 1rem;
76 | color: #fff;
77 | background: #5372F0;
78 | transition: 0.2s ease;
79 | }
80 |
81 | .weather-input .search-btn:hover {
82 | background: #2c52ed;
83 | }
84 |
85 | .weather-input .location-btn {
86 | background: #6C757D;
87 | }
88 |
89 | .weather-input .location-btn:hover {
90 | background: #5c636a;
91 | }
92 |
93 | .weather-data {
94 | width: 100%;
95 | }
96 |
97 | .weather-data .current-weather {
98 | color: #fff;
99 | background: #5372F0;
100 | border-radius: 5px;
101 | padding: 20px 70px 20px 20px;
102 | display: flex;
103 | justify-content: space-between;
104 | }
105 |
106 | .current-weather h2 {
107 | font-weight: 700;
108 | font-size: 1.7rem;
109 | }
110 |
111 | .weather-data h6 {
112 | margin-top: 12px;
113 | font-size: 1rem;
114 | font-weight: 500;
115 | }
116 |
117 | .current-weather .icon {
118 | text-align: center;
119 | }
120 |
121 | .current-weather .icon img {
122 | max-width: 120px;
123 | margin-top: -15px;
124 | }
125 |
126 | .current-weather .icon h6 {
127 | margin-top: -10px;
128 | text-transform: capitalize;
129 | }
130 |
131 | .days-forecast h2 {
132 | margin: 20px 0;
133 | font-size: 1.5rem;
134 | }
135 |
136 | .days-forecast .weather-cards {
137 | display: flex;
138 | gap: 20px;
139 | }
140 |
141 | .weather-cards .card {
142 | color: #fff;
143 | padding: 18px 16px;
144 | list-style: none;
145 | width: calc(100% / 5);
146 | background: #6C757D;
147 | border-radius: 5px;
148 | }
149 |
150 | .weather-cards .card h3 {
151 | font-size: 1.3rem;
152 | font-weight: 600;
153 | }
154 |
155 | .weather-cards .card img {
156 | max-width: 70px;
157 | margin: 5px 0 -12px 0;
158 | }
159 |
160 | @media (max-width: 1400px) {
161 | .weather-data .current-weather {
162 | padding: 20px;
163 | }
164 |
165 | .weather-cards {
166 | flex-wrap: wrap;
167 | }
168 |
169 | .weather-cards .card {
170 | width: calc(100% / 4 - 15px);
171 | }
172 | }
173 |
174 | @media (max-width: 1200px) {
175 | .weather-cards .card {
176 | width: calc(100% / 3 - 15px);
177 | }
178 | }
179 |
180 | @media (max-width: 950px) {
181 | .weather-input {
182 | width: 450px;
183 | }
184 |
185 | .weather-cards .card {
186 | width: calc(100% / 2 - 10px);
187 | }
188 | }
189 |
190 | @media (max-width: 750px) {
191 | h1 {
192 | font-size: 1.45rem;
193 | padding: 16px 0;
194 | }
195 |
196 | .container {
197 | flex-wrap: wrap;
198 | padding: 15px;
199 | }
200 |
201 | .weather-input {
202 | width: 100%;
203 | }
204 |
205 | .weather-data h2 {
206 | font-size: 1.35rem;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | const cityInput = document.querySelector(".city-input");
2 | const searchButton = document.querySelector(".search-btn");
3 | const locationButton = document.querySelector(".location-btn");
4 | const currentWeatherDiv = document.querySelector(".current-weather");
5 | const weatherCardsDiv = document.querySelector(".weather-cards");
6 |
7 | const createWeatherCard = (city, weatherData, index) => {
8 | if (index === 0) {
9 | return `
10 |
11 |
${city} (${weatherData.dt_txt.split(" ")[0]})
12 | Temperature: ${(weatherData.main.temp - 273.15).toFixed(2)}°C
13 | Wind: ${weatherData.wind.speed} M/S
14 | Humidity: ${weatherData.main.humidity}%
15 |
16 |
17 |
18 |
${weatherData.weather[0].description}
19 |
`;
20 | } else {
21 | return `
22 |
23 | (${weatherData.dt_txt.split(" ")[0]})
24 |
25 | Temp: ${(weatherData.main.temp - 273.15).toFixed(2)}°C
26 | Wind: ${weatherData.wind.speed} M/S
27 | Humidity: ${weatherData.main.humidity}%
28 | `;
29 | }
30 | };
31 |
32 | const getWeatherDetails = (city, lat, lon) => {
33 | const apiKey = "267cf21554b098b03a55b035485b7dc6";
34 | const url = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}`;
35 |
36 | fetch(url)
37 | .then(response => response.json())
38 | .then(data => {
39 | const uniqueDates = [];
40 | const filteredData = data.list.filter(item => {
41 | const date = new Date(item.dt_txt).getDate();
42 | if (!uniqueDates.includes(date)) {
43 | uniqueDates.push(date);
44 | return true;
45 | }
46 | return false;
47 | });
48 |
49 | cityInput.value = '';
50 | currentWeatherDiv.innerHTML = '';
51 | weatherCardsDiv.innerHTML = '';
52 |
53 | filteredData.forEach((weatherData, index) => {
54 | const weatherCard = createWeatherCard(city, weatherData, index);
55 | if (index === 0) {
56 | currentWeatherDiv.insertAdjacentHTML("beforeend", weatherCard);
57 | } else {
58 | weatherCardsDiv.insertAdjacentHTML("beforeend", weatherCard);
59 | }
60 | });
61 | })
62 | .catch(() => {
63 | alert("Terjadi kesalahan saat mengambil ramalan cuaca!");
64 | });
65 | };
66 |
67 | const getCityCoordinates = () => {
68 | const cityName = cityInput.value.trim();
69 | if (cityName === '') return;
70 |
71 | const apiKey = "267cf21554b098b03a55b035485b7dc6";
72 | const url = `https://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=1&appid=${apiKey}`;
73 |
74 | fetch(url)
75 | .then(response => response.json())
76 | .then(data => {
77 | if (!data.length) {
78 | alert(`Harap coba lagi, Tidak ada koordinat yang ditemukan untuk ${cityName}`);
79 | return;
80 | }
81 |
82 | const { lat, lon, name } = data[0];
83 | getWeatherDetails(name, lat, lon);
84 | })
85 | .catch(() => {
86 | alert("Harap coba lagi, Terjadi kesalahan saat mengambil koordinat!");
87 | });
88 | };
89 |
90 | const getUserLocation = () => {
91 | if (navigator.geolocation) {
92 | navigator.geolocation.getCurrentPosition(
93 | (position) => {
94 | const { latitude, longitude } = position.coords;
95 | getWeatherDetails("Lokasi Anda", latitude, longitude);
96 | },
97 | () => {
98 | alert("Gagal mendapatkan loasimu!");
99 | }
100 | );
101 | } else {
102 | alert("Geolocation tidak dapat digunakan di browser Anda!");
103 | }
104 | };
105 |
106 | searchButton.addEventListener("click", getCityCoordinates);
107 | locationButton.addEventListener("click", getUserLocation);
108 |
--------------------------------------------------------------------------------