Skip to content

Commit 70c7c69

Browse files
authored
Integrate OpenWeather API and enhance UI features
Refactored weather fetching logic and added support for OpenWeather API. Introduced temperature unit toggle and improved error handling.
1 parent b792fb8 commit 70c7c69

File tree

1 file changed

+245
-45
lines changed

1 file changed

+245
-45
lines changed

Javascript/Weather Site/script.js

Lines changed: 245 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,261 @@
1-
const API_KEY = "f45d51406f914de6899151327251010";
2-
const BASE = "https://api.weatherapi.com/v1/current.json"; // Use HTTPS to avoid browser blocks
1+
let currentUnit = 'celsius';
2+
let currentWeatherData = null;
3+
let currentForecastData = null;
4+
const API_KEY = 'bd5e378503939ddaee76f12ad7a97608';
5+
const WEATHER_API_URL = 'https://api.openweathermap.org/data/2.5/weather';
6+
const FORECAST_API_URL = 'https://api.openweathermap.org/data/2.5/forecast';
37

4-
const $ = (id) => document.getElementById(id);
5-
const searchBtn = $("search");
6-
const qInput = $("q");
7-
const loadingEl = $("loading");
8-
const errorEl = $("error");
8+
// Weather condition to icon mapping
9+
const weatherIcons = {
10+
'Clear': '☀️',
11+
'Clouds': '☁️',
12+
'Rain': '🌧️',
13+
'Drizzle': '🌦️',
14+
'Thunderstorm': '⛈️',
15+
'Snow': '🌨️',
16+
'Mist': '🌫️',
17+
'Fog': '🌫️',
18+
'Haze': '🌫️'
19+
};
920

10-
async function fetchWeather(q) {
11-
errorEl.style.display = "none";
12-
loadingEl.style.display = "block";
21+
// Gradient colors for different weather conditions
22+
const weatherGradients = {
23+
'Clear': 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #ea580c 100%)',
24+
'Clouds': 'linear-gradient(135deg, #6b7280 0%, #4b5563 50%, #374151 100%)',
25+
'Rain': 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1e40af 100%)',
26+
'Drizzle': 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1e40af 100%)',
27+
'Thunderstorm': 'linear-gradient(135deg, #1f2937 0%, #374151 50%, #4b5563 100%)',
28+
'Snow': 'linear-gradient(135deg, #e5e7eb 0%, #d1d5db 50%, #9ca3af 100%)',
29+
'default': 'linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #ec4899 100%)'
30+
};
1331

14-
try {
15-
const url = `${BASE}?key=${API_KEY}&q=${encodeURIComponent(q)}&aqi=yes`;
16-
const res = await fetch(url);
32+
// DOM elements
33+
const elements = {
34+
loading: document.getElementById('loading'),
35+
errorMessage: document.getElementById('errorMessage'),
36+
cityInput: document.getElementById('cityInput'),
37+
searchBtn: document.getElementById('searchBtn'),
38+
cityName: document.getElementById('cityName'),
39+
dateTime: document.getElementById('dateTime'),
40+
weatherIcon: document.getElementById('weatherIcon'),
41+
temperature: document.getElementById('temperature'),
42+
tempToggle: document.getElementById('tempToggle'),
43+
weatherDescription: document.getElementById('weatherDescription'),
44+
feelsLike: document.getElementById('feelsLike'),
45+
humidity: document.getElementById('humidity'),
46+
windSpeed: document.getElementById('windSpeed'),
47+
pressure: document.getElementById('pressure'),
48+
visibility: document.getElementById('visibility'),
49+
uvIndex: document.getElementById('uvIndex'),
50+
forecastContainer: document.getElementById('forecastContainer')
51+
};
1752

18-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
53+
// Utility functions
54+
function showLoading() {
55+
elements.loading.style.display = 'flex';
56+
}
57+
58+
function hideLoading() {
59+
elements.loading.style.display = 'none';
60+
}
61+
62+
function showError(message) {
63+
elements.errorMessage.textContent = message;
64+
elements.errorMessage.style.display = 'block';
65+
setTimeout(() => {
66+
elements.errorMessage.style.display = 'none';
67+
}, 5000);
68+
}
69+
70+
function getWeatherIcon(condition) {
71+
return weatherIcons[condition] || '🌤️';
72+
}
73+
74+
function convertTemp(temp, unit) {
75+
if (unit === 'fahrenheit') {
76+
return Math.round((temp * 9/5) + 32);
77+
}
78+
return Math.round(temp);
79+
}
1980

20-
const data = await res.json();
21-
render(data);
22-
} catch (err) {
23-
showError(err.message);
24-
} finally {
25-
loadingEl.style.display = "none";
26-
}
81+
function formatDate() {
82+
const now = new Date();
83+
const options = {
84+
weekday: 'long',
85+
year: 'numeric',
86+
month: 'long',
87+
day: 'numeric',
88+
hour: '2-digit',
89+
minute: '2-digit'
90+
};
91+
return now.toLocaleDateString('en-US', options);
2792
}
2893

29-
function render(data) {
30-
if (!data || !data.location) return showError("Invalid response");
94+
function updateBackground(weatherCondition) {
95+
const gradient = weatherGradients[weatherCondition] || weatherGradients.default;
96+
document.body.style.background = gradient;
97+
document.body.style.backgroundSize = '400% 400%';
98+
}
3199

32-
$("location").textContent = `${data.location.name}, ${data.location.country}`;
33-
$("localtime").textContent = data.location.localtime || "--";
34-
$("temp").textContent = `${data.current.temp_c}°C`;
35-
$("condition").textContent = data.current.condition.text;
36-
$("humidity").textContent = `${data.current.humidity}%`;
37-
$("wind").textContent = `${data.current.wind_kph} kph`;
38-
$("feelslike").textContent = `${data.current.feelslike_c}°C`;
100+
// API functions
101+
async function fetchWeather(city) {
102+
try {
103+
const response = await fetch(`${WEATHER_API_URL}?q=${city}&appid=${API_KEY}&units=metric`);
104+
if (!response.ok) {
105+
throw new Error(`Weather data not found for "${city}". Please check the city name and try again.`);
106+
}
107+
const data = await response.json();
108+
return data;
109+
} catch (error) {
110+
throw new Error(error.message || 'Failed to fetch weather data. Please try again.');
111+
}
112+
}
39113

40-
const iconUrl = data.current.condition.icon.startsWith("//")
41-
? "https:" + data.current.condition.icon
42-
: data.current.condition.icon;
114+
async function fetchForecast(city) {
115+
try {
116+
const response = await fetch(`${FORECAST_API_URL}?q=${city}&appid=${API_KEY}&units=metric`);
117+
if (!response.ok) {
118+
throw new Error('Failed to fetch forecast data');
119+
}
120+
const data = await response.json();
121+
return data;
122+
} catch (error) {
123+
throw new Error('Failed to fetch forecast data. Please try again.');
124+
}
125+
}
43126

44-
$("icon").src = iconUrl;
45-
$("icon").alt = data.current.condition.text;
127+
// Display functions
128+
function displayWeather(data) {
129+
currentWeatherData = data;
130+
131+
elements.cityName.textContent = `${data.name}, ${data.sys.country}`;
132+
elements.dateTime.textContent = formatDate();
133+
134+
const condition = data.weather[0].main;
135+
elements.weatherIcon.textContent = getWeatherIcon(condition);
136+
137+
const temp = convertTemp(data.main.temp, currentUnit);
138+
const unit = currentUnit === 'celsius' ? '°C' : '°F';
139+
elements.temperature.textContent = `${temp}${unit}`;
140+
141+
elements.weatherDescription.textContent = data.weather[0].description;
142+
143+
// Update details
144+
const feelsLikeTemp = convertTemp(data.main.feels_like, currentUnit);
145+
elements.feelsLike.textContent = `${feelsLikeTemp}${unit}`;
146+
elements.humidity.textContent = `${data.main.humidity}%`;
147+
elements.windSpeed.textContent = `${Math.round(data.wind.speed * 10) / 10} m/s`;
148+
elements.pressure.textContent = `${data.main.pressure} hPa`;
149+
elements.visibility.textContent = data.visibility ? `${Math.round(data.visibility / 1000)} km` : 'N/A';
150+
151+
// UV Index is not available in the free API, so we'll show N/A
152+
elements.uvIndex.textContent = 'N/A';
153+
154+
// Update background based on weather
155+
updateBackground(condition);
46156
}
47157

48-
function showError(msg) {
49-
errorEl.style.display = "block";
50-
errorEl.className = "error";
51-
errorEl.textContent = msg;
158+
function displayForecast(data) {
159+
currentForecastData = data;
160+
elements.forecastContainer.innerHTML = '';
161+
162+
// Group forecast data by day (every 8th item represents a new day as data is every 3 hours)
163+
const dailyForecasts = [];
164+
for (let i = 0; i < data.list.length; i += 8) {
165+
if (dailyForecasts.length >= 5) break;
166+
dailyForecasts.push(data.list[i]);
167+
}
168+
169+
dailyForecasts.forEach((forecast, index) => {
170+
const forecastCard = document.createElement('div');
171+
forecastCard.className = 'glass-card forecast-card';
172+
173+
const date = new Date(forecast.dt * 1000);
174+
const dayName = index === 0 ? 'Today' : date.toLocaleDateString('en-US', { weekday: 'long' });
175+
176+
const condition = forecast.weather[0].main;
177+
const icon = getWeatherIcon(condition);
178+
const highTemp = convertTemp(forecast.main.temp_max, currentUnit);
179+
const lowTemp = convertTemp(forecast.main.temp_min, currentUnit);
180+
const unit = currentUnit === 'celsius' ? '°C' : '°F';
181+
182+
forecastCard.innerHTML = `
183+
<div class="forecast-day">${dayName}</div>
184+
<div class="forecast-icon">${icon}</div>
185+
<div class="forecast-description">${forecast.weather[0].description}</div>
186+
<div class="forecast-temps">
187+
<span class="high-temp">${highTemp}${unit}</span>
188+
<span class="low-temp">${lowTemp}${unit}</span>
189+
</div>
190+
`;
191+
192+
elements.forecastContainer.appendChild(forecastCard);
193+
});
52194
}
53195

54-
searchBtn.addEventListener("click", () =>
55-
fetchWeather(qInput.value || "London")
56-
);
57-
qInput.addEventListener("keydown", (e) => {
58-
if (e.key === "Enter") fetchWeather(qInput.value || "London");
196+
function updateTemperatureDisplay() {
197+
if (currentWeatherData) {
198+
displayWeather(currentWeatherData);
199+
}
200+
if (currentForecastData) {
201+
displayForecast(currentForecastData);
202+
}
203+
}
204+
205+
// Main functions
206+
async function loadWeatherData(city) {
207+
showLoading();
208+
elements.errorMessage.style.display = 'none';
209+
210+
try {
211+
const [weatherData, forecastData] = await Promise.all([
212+
fetchWeather(city),
213+
fetchForecast(city)
214+
]);
215+
216+
displayWeather(weatherData);
217+
displayForecast(forecastData);
218+
} catch (error) {
219+
showError(error.message);
220+
} finally {
221+
hideLoading();
222+
}
223+
}
224+
225+
function handleSearch() {
226+
const city = elements.cityInput.value.trim();
227+
if (city) {
228+
loadWeatherData(city);
229+
elements.cityInput.value = '';
230+
}
231+
}
232+
233+
function toggleTemperatureUnit() {
234+
currentUnit = currentUnit === 'celsius' ? 'fahrenheit' : 'celsius';
235+
elements.tempToggle.textContent = currentUnit === 'celsius' ? '°F' : '°C';
236+
updateTemperatureDisplay();
237+
}
238+
239+
// Update time every minute
240+
function updateTime() {
241+
elements.dateTime.textContent = formatDate();
242+
}
243+
244+
// Event listeners
245+
elements.searchBtn.addEventListener('click', handleSearch);
246+
elements.cityInput.addEventListener('keypress', (e) => {
247+
if (e.key === 'Enter') {
248+
handleSearch();
249+
}
59250
});
251+
elements.tempToggle.addEventListener('click', toggleTemperatureUnit);
60252

61-
fetchWeather(qInput.value || "London");
253+
// Initialize app
254+
document.addEventListener('DOMContentLoaded', () => {
255+
// Load default city (London)
256+
loadWeatherData('London');
257+
258+
// Update time every minute
259+
setInterval(updateTime, 60000);
260+
updateTime();
261+
});

0 commit comments

Comments
 (0)