├── 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 | HTML5 39 | CSS3 40 | JavaScript 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 |
23 |

Masukkan Nama Kota Anda.

24 | 25 | 26 |
27 | 28 |
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 | weather-icon 18 |
${weatherData.weather[0].description}
19 |
`; 20 | } else { 21 | return ` 22 |
  • 23 |

    (${weatherData.dt_txt.split(" ")[0]})

    24 | weather-icon 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 | --------------------------------------------------------------------------------