如何在基于OpenWeatherMap API的JS天气应用中限制搜索美国城市并处理日温数据
问题描述
我为课程作业开发了一款基于OpenWeatherMap API的极简天气应用,目前面临两个技术问题:
- 按要求使用5-day/3-hour forecast API展示未来5天的最高/最低气温、湿度、天气状况及对应图标,但该API以3小时为间隔返回数据,无法直接获取每日的最高、最低气温,当前手动创建每日元素并取固定索引数据的方式十分笨拙;
- 需要将城市搜索范围限制为美国城市,但不清楚如何在JavaScript中实现,也未找到API调用时的对应参数设置方法。
当前应用代码如下:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Weather app</title> </head> <body> <h1>Weather forecast</h1> <form id="cityForm"> <label for="city">Enter City Name:</label> <input type="text" id="city" name="city" /> <br /><br /> <input type="submit" value="Submit" /> </form> <div id="output"></div> <script> function getWeather(query) { var url = "http://api.openweathermap.org/data/2.5/forecast?q=" + query + "&units=imperial&APPID=56866f8407bb1e2d63330f3c046f35af"; fetch(url) .then((response) => response.json()) .then((data) => displayWeather(data)); //call displayWeather function } document .getElementById("cityForm") .addEventListener("submit", function (event) { event.preventDefault(); // prevent the form from submitting var city = document.getElementById("city").value; // get the city name from the input field getWeather(city); }); function displayWeather(weather) { console.log(weather); var resultContainer = document.querySelector("#output"); var cityName = document.createElement("h2"); resultContainer.append(cityName); var forecastDate1 = document.createElement("p"); // Creating elements var forecastDate2 = document.createElement("p"); var forecastDate3 = document.createElement("p"); var forecastDate4 = document.createElement("p"); var forecastDate5 = document.createElement("p"); cityName.textContent = weather.city.name; var icon = document.createElement("img"); var iconUrl = "http://openweathermap.org/img/w/" + weather.list[0].weather[0].icon + ".png"; icon.setAttribute("src", iconUrl); var description = document.createElement("p"); var day1 = new Date(weather.list[0].dt * 1000); const month1 = day1.getMonth() + 1; const firstDay = day1.getDate(); const year1 = day1.getFullYear(); const outputDateStr1 = `${month1.toString().padStart(2, "0")}/${firstDay .toString() .padStart(2, "0")}/${year1.toString()}`; var day2 = new Date(weather.list[8].dt * 1000); const month2 = day2.getMonth() + 1; const secondDay = day2.getDate(); const year2 = day2.getFullYear(); const outputDateStr2 = `${month2 .toString() .padStart(2, "0")}/${secondDay .toString() .padStart(2, "0")}/${year2.toString()}`; var day3 = new Date(weather.list[16].dt * 1000); const month3 = day3.getMonth() + 1; const thirdDay = day3.getDate(); const year3 = day3.getFullYear(); const outputDateStr3 = `${month3.toString().padStart(2, "0")}/${thirdDay .toString() .padStart(2, "0")}/${year3.toString()}`; var day4 = new Date(weather.list[24].dt * 1000); const month4 = day4.getMonth() + 1; const fourthDay = day4.getDate(); const year4 = day4.getFullYear(); const outputDateStr4 = `${month4 .toString() .padStart(2, "0")}/${fourthDay .toString() .padStart(2, "0")}/${year4.toString()}`; var day5 = new Date(weather.list[32].dt * 1000); const month5 = day5.getMonth() + 1; const fifthDay = day5.getDate(); const year5 = day5.getFullYear(); const outputDateStr5 = `${month5.toString().padStart(2, "0")}/${fifthDay .toString() .padStart(2, "0")}/${year5.toString()}`; forecastDate1.textContent = `${outputDateStr1} Min ${weather.list[0].main.temp_min} F Max: ${weather.list[0].main.temp_max} F Humidity: ${weather.list[0].main.humidity}% Weather conditions: ${weather.list[0].weather[0].description}`; var icon1 = document.createElement("img"); var iconUrl1 = "http://openweathermap.org/img/w/" + weather.list[0].weather[0].icon + ".png"; icon1.setAttribute("src", iconUrl1); forecastDate1.append(icon1); forecastDate2.textContent = `${outputDateStr2} Min ${weather.list[8].main.temp_min} F Max: ${weather.list[8].main.temp_max} F Humidity: ${weather.list[8].main.humidity}% Weather conditions: ${weather.list[8].weather[0].description}`; var icon2 = document.createElement("img"); var iconUrl2 = "http://openweathermap.org/img/w/" + weather.list[8].weather[0].icon + ".png"; icon2.setAttribute("src", iconUrl2); forecastDate2.append(icon2); forecastDate3.textContent = `${outputDateStr3} Min ${weather.list[16].main.temp_min} F Max: ${weather.list[16].main.temp_max} F Humidity: ${weather.list[16].main.humidity}% Weather conditions: ${weather.list[16].weather[0].description}`; var icon3 = document.createElement("img"); var iconUrl3 = "http://openweathermap.org/img/w/" + weather.list[16].weather[0].icon + ".png"; icon3.setAttribute("src", iconUrl3); forecastDate3.append(icon3); forecastDate4.textContent = `${outputDateStr4} Min ${weather.list[24].main.temp_min} F Max: ${weather.list[24].main.temp_max} F Humidity: ${weather.list[24].main.humidity}% Weather conditions: ${weather.list[24].weather[0].description}`; var icon4 = document.createElement("img"); var iconUrl4 = "http://openweathermap.org/img/w/" + weather.list[24].weather[0].icon + ".png"; icon4.setAttribute("src", iconUrl4); forecastDate4.append(icon4); forecastDate5.textContent = `${outputDateStr5} Min ${weather.list[32].main.temp_min} F Max: ${weather.list[32].main.temp_max} F Humidity: ${weather.list[32].main.humidity}% Weather conditions: ${weather.list[32].weather[0].description}`; var icon5 = document.createElement("img"); var iconUrl5 = "http://openweathermap.org/img/w/" + weather.list[32].weather[0].icon + ".png"; icon5.setAttribute("src", iconUrl5); forecastDate5.append(icon5); resultContainer.append(forecastDate1); resultContainer.append(forecastDate2); resultContainer.append(forecastDate3); resultContainer.append(forecastDate4); resultContainer.append(forecastDate5); } </script> </body> </html>
API返回数据说明:返回的list数组包含40条3小时间隔的数据,每条数据包含dt(时间戳)、main(气温、湿度等)、weather(天气状况及图标)等字段。
解决方案
问题1:处理每日最高/最低气温,优化渲染逻辑
实现思路
先将API返回的3小时数据按日期分组,再对每组数据计算当日最高温、最低温,选取当日代表性时段(如中午)的天气图标和描述,最后动态生成DOM元素,避免重复代码。
代码替换
将原有的displayWeather函数替换为以下代码:
function displayWeather(weather) { const resultContainer = document.querySelector("#output"); resultContainer.innerHTML = ""; // 清空历史结果 // 1. 按日期分组数据 const dailyGroups = {}; weather.list.forEach(item => { const date = new Date(item.dt * 1000); // 生成YYYY-MM-DD格式的日期作为分组键 const dateKey = `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`; if (!dailyGroups[dateKey]) { dailyGroups[dateKey] = []; } dailyGroups[dateKey].push(item); }); // 2. 生成城市标题 const cityName = document.createElement("h2"); cityName.textContent = weather.city.name; resultContainer.append(cityName); // 3. 遍历分组,处理每日数据 Object.values(dailyGroups).slice(0,5).forEach(dayData => { // 取前5天数据 // 计算当日最高/最低温 const temps = dayData.map(item => item.main.temp); const maxTemp = Math.max(...temps); const minTemp = Math.min(...temps); // 取当天第5条数据(对应12点左右)作为天气状况展示,若不存在则取第一条 const representativeItem = dayData[4] || dayData[0]; const humidity = representativeItem.main.humidity; const weatherDesc = representativeItem.weather[0].description; const iconCode = representativeItem.weather[0].icon; // 格式化日期 const date = new Date(dayData[0].dt * 1000); const dateStr = `${String(date.getMonth()+1).padStart(2, "0")}/${String(date.getDate()).padStart(2, "0")}/${date.getFullYear()}`; // 创建DOM元素 const forecastItem = document.createElement("p"); forecastItem.innerHTML = `${dateStr} Min ${minTemp.toFixed(1)} F Max: ${maxTemp.toFixed(1)} F Humidity: ${humidity}% Weather conditions: ${weatherDesc}`; const icon = document.createElement("img"); icon.src = `http://openweathermap.org/img/w/${iconCode}.png`; forecastItem.append(icon); resultContainer.append(forecastItem); }); }
问题2:限制搜索范围为美国城市
实现思路
OpenWeatherMap的forecastAPI支持country参数,指定国家代码为US即可限制搜索范围;同时在API返回后校验城市所属国家,做双重保障。
代码修改
- 修改
getWeather函数的API请求URL,添加&country=US参数:
function getWeather(query) { // 添加country=US参数限制美国城市 var url = "http://api.openweathermap.org/data/2.5/forecast?q=" + query + "&country=US&units=imperial&APPID=56866f8407bb1e2d63330f3c046f35af"; fetch(url) .then((response) => response.json()) .then((data) => { // 额外校验:若返回城市的国家代码不是US,提示用户 if (data.city && data.city.country !== "US") { const resultContainer = document.querySelector("#output"); resultContainer.innerHTML = "<p>请输入美国境内的城市名称</p>"; return; } displayWeather(data); }) .catch(err => { const resultContainer = document.querySelector("#output"); resultContainer.innerHTML = "<p>无法获取天气数据,请检查城市名称是否正确</p>"; }); }
- 可选优化:修改表单提示,引导用户输入美国城市,比如将label改为:
<label for="city">Enter US City Name:</label>
内容的提问来源于stack exchange,提问作者user19334054
相关产品推荐
相关产品推荐

