目录
[三、HTML5 页面制作](#三、HTML5 页面制作)
哈喽各位小伙伴,大家好!今天是2026年6月7日,高考的第一天。关于高考的话题热度一直居高不下,作为一名开发者,今天就借着这个热点,手把手带大家做一个湖南各地市高考天气查询页面。项目基于原生 HTML5 + JavaScript,对接百度天气 API,同时结合本地 JS 数据完成解析渲染,上手简单、实用性拉满,新手也能跟着练,话不多说,直接进入正文!

一、前言
每年高考前夕,天气都是考生、家长和考务人员最关心的问题之一。湖南地域跨度不小,省内各地气温、降水差异较大,像湘南、湘西部分区域近期雨水偏多,高温、降雨都会直接影响考生出行和临场状态。正好博主之前讲解了多篇关于百度天气API应用接口,手上整理了湖南多个地市的百度天气接口原始数据,索性就用原生 HTML5 搭建一个简易天气展示页。不用复杂框架,纯前端原生代码,既能练一练接口数据解析、DOM 动态渲染,又能做出一个实实在在能用的小工具。下面我就把完整思路、代码和最终效果分享出来。
二、项目应用场景
麻雀虽小五脏俱全,这个小项目看着简单,实际使用场景还挺多,不光是练手 demo:
- 考生 & 家长使用:快速查询考点城市实时天气、未来一周预报,提前备好雨具、防晒用品。
- 考务工作辅助:工作人员可以批量查看多地天气,提前做好防雨、防暑等应急准备。
- 前端练手案例 :非常适合前端入门同学,学习第三方 API 数据解析、JSON 数据处理、页面动态渲染,是典型的原生前端实战小项目。
整体代码轻量化,无需搭建复杂环境,本地打开 HTML 文件就能运行,部署到静态服务器也毫无压力。
bash
┌─────────────────────────────────────────────────────────────┐
│ 浏览器渲染层 │
│ (HTML 结构 + CSS 样式 + Chart.js 图表可视化) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ JS 核心渲染引擎 │
│ ├─ 数据格式化工具(日期、AQI、天气文字、图标映射) │
│ ├─ DOM 动态填充(温度、湿度、风力、气压、生活指数) │
│ └─ 图表生成(24小时气温/降水概率 + 小时降水量柱状图) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ 天气数据源层 │
│ ├─ 静态数据:weather-data-xxxx.js(百度天气API原始数据) │
│ └─ 动态数据:百度天气 API v1 实时接口(可扩展) │
└─────────────────────────────────────────────────────────────┘
三、HTML5 页面制作
整个项目采用原生 HTML + CSS + JavaScript 开发,没有引入任何框架、插件,最大程度还原前端基础写法,方便大家学习和二次修改。
bash
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 页面展示层 │ │ 逻辑控制层 │ │ 数据来源层 │
│ (HTML/CSS/Chart)│◄────────┤ (render 主函数) │◄───────┤ JS静态数据文件 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
▲ ▲
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ DOM 节点填充 │ │ 数据格式转换工具 │
└─────────────────┘ └──────────────────┘
1、页面整体框架
页面布局主打简约实用,分为标题区、天气展示卡片、7日温度趋势、逐小时天气气温信息、生活指数,。 下面是完整页面骨架代码,结构清晰,注释也写得很详细:
bash
/
├─ index.html # 主页面(结构 + 样式)
├─ weather-data-430100.js # 长沙天气数据
├─ weather-data-430200.js # 株洲
├─ weather-data-430300.js # 湘潭
├─ weather-data-430400.js # 衡阳
├─ weather-data-430500.js # 邵阳
├─ weather-data-430600.js # 岳阳
├─ ...(其他湖南地市)
└─ README.md # 使用说明(可选)
bash
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气预报 | 气象服务</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script src="weather-data.js"></script>
<style>
</style>
</head>
<body>
<!-- 初始加载占位 -->
<div class="loading" id="loading">正在加载天气数据...</div>
<!-- 页面骨架(数据注入后替换 loading) -->
<div class="shell" id="app" style="display:none;">
<!-- TOP BAR -->
<header class="topbar">
<div class="topbar-badge">气象服务 · 实况播报</div>
<div class="topbar-title" id="tb-title"></div>
<div class="topbar-sub" id="tb-sub"></div>
<div class="topbar-time" id="tb-time"></div>
</header>
<!-- ALERT BAR -->
<div class="alert-bar safe" id="alert-bar">
<div class="alert-dot"></div>
<span class="alert-status" id="alert-status"></span>
<span class="alert-desc" id="alert-desc"></span>
<span class="alert-time" id="alert-time"></span>
</div>
<!-- MAIN 3-COL x 2-ROW GRID -->
<div class="main">
<!-- 1 LEFT: current + AQI -->
<div class="col-now">
<div class="card now-card" style="flex:1.4;">
<div class="now-temp" id="now-temp"><span class="now-unit">℃</span></div>
<div class="now-weather" id="now-weather"></div>
<div class="now-feel" id="now-feel"></div>
<div class="now-divider"></div>
<div class="meta-row">
<div class="meta-icon">💨</div>
<div><div class="meta-lbl">风向 / 风力</div><div class="meta-val" id="now-wind"></div></div>
</div>
<div class="meta-row">
<div class="meta-icon">👁</div>
<div><div class="meta-lbl">能见度</div><div class="meta-val" id="now-vis"></div></div>
</div>
<div class="meta-row">
<div class="meta-icon">🌡</div>
<div><div class="meta-lbl">气压 / 露点</div><div class="meta-val" id="now-pressure"></div></div>
</div>
<div class="meta-row" style="margin-bottom:0;">
<div class="meta-icon">☀</div>
<div><div class="meta-lbl">紫外线指数</div><div class="meta-val" id="now-uvi"></div></div>
</div>
</div>
<!-- AQI -->
<div class="card" style="flex:0.9;">
<div class="aqi-row">
<div>
<div class="aqi-lbl">空气质量</div>
<div class="aqi-num" id="aqi-num"></div>
<div class="aqi-tag" id="aqi-tag"></div>
</div>
<div style="flex:1;">
<div class="aqi-track"><div class="aqi-dot" id="aqi-dot"></div></div>
<div style="display:flex;justify-content:space-between;margin-top:6px;">
<span style="font-size:10px;color:var(--td);">优</span>
<span style="font-size:10px;color:var(--td);">良</span>
<span style="font-size:10px;color:var(--td);">轻度</span>
<span style="font-size:10px;color:var(--td);">中度</span>
<span style="font-size:10px;color:var(--td);">严重</span>
</div>
</div>
</div>
<div class="poll-grid" id="poll-grid"></div>
</div>
</div>
<!-- 2 MIDDLE-TOP: 7-day forecast -->
<div class="col-7day card" style="padding:16px 18px;">
<div class="sec-hd">
<div class="sec-line"></div>
<span class="sec-title">7日天气预报</span>
<span class="sec-sub" id="fc-range"></span>
</div>
<div class="forecast-row" id="forecast-row"></div>
</div>
<!-- 3 RIGHT-TOP: life indexes -->
<div class="col-index">
<div class="sec-hd" style="margin-bottom:0;padding:0 2px;">
<div class="sec-line"></div>
<span class="sec-title">生活指数</span>
<span class="sec-sub">今日建议</span>
</div>
<div class="idx-list" id="idx-list"></div>
</div>
<!-- 4 MIDDLE-BOTTOM: temp + hourly charts -->
<div class="col-hourly card" style="padding:16px 18px;display:flex;flex-direction:column;gap:10px;">
<div style="display:flex;gap:12px;flex:1;min-height:0;">
<div style="flex:1;display:flex;flex-direction:column;">
<div class="sec-hd" style="margin-bottom:4px;">
<div class="sec-line"></div>
<span class="sec-title">7日温度趋势</span>
<div class="legend-inline">
<span><span class="legend-swatch" style="background:#ea580c;"></span>最高</span>
<span><span class="legend-swatch" style="background:#2563eb;"></span>最低</span>
</div>
</div>
<div style="flex:1;position:relative;min-height:0;">
<canvas id="tempChart" role="img" aria-label="7日温度趋势折线图">7日温度趋势</canvas>
</div>
</div>
<div style="width:1px;background:var(--bdr);flex-shrink:0;"></div>
<div style="flex:1.4;display:flex;flex-direction:column;">
<div class="sec-hd" style="margin-bottom:4px;">
<div class="sec-line"></div>
<span class="sec-title">逐小时气温 & 降水概率</span>
<div class="legend-inline">
<span><span class="legend-swatch" style="background:#0891b2;"></span>℃</span>
<span><span class="legend-swatch" style="background:#2563eb;border:1px dashed #2563eb;background:transparent;width:12px;height:0;border-radius:0;"></span>%</span>
</div>
</div>
<div style="flex:1;position:relative;min-height:0;">
<canvas id="hourlyChart" role="img" aria-label="逐小时气温与降水概率">逐小时预报</canvas>
</div>
</div>
</div>
</div>
<!-- 5 RIGHT-BOTTOM: precipitation bars -->
<div class="col-prec card" style="padding:16px 18px;">
<div class="sec-hd" style="margin-bottom:4px;">
<div class="sec-line"></div>
<span class="sec-title">逐小时降水量</span>
<span class="sec-sub">mm/h · 未来24h</span>
</div>
<div style="flex:1;position:relative;min-height:0;height:calc(100% - 34px);">
<canvas id="precChart" role="img" aria-label="逐小时降水量柱状图">逐小时降水量</canvas>
</div>
</div>
</div>
<!-- FOOTER -->
<footer class="footer">
<span>数据来源:<strong>百度地图天气 API</strong></span>
<span>·</span>
<span id="footer-loc"></span>
<span>·</span>
<span>本预报仅供参考,请以当地气象部门官方发布为准</span>
</footer>
</div>
<script>
/* ================================================================
动态渲染引擎 --- 从 weather-data.json 读取数据后填充 DOM 和图表
================================================================ */
// 核心逻辑JS写在这里
</script>
</body>
</html>
布局思路很简单:外层容器做卡片美化,中间区域专门用来动态拼接 HTML,展示解析后的天气数据。
bash
页面加载
↓
IIFE 入口
↓
数据格式识别(百度API / 自定义格式)
↓
数据清洗、字段补全
↓
render(d) 【主渲染】
├─ 填充实况信息
├─ 填充AQI
├─ 填充7日预报
├─ 填充生活指数
└─ 绘制两张图表(气温 + 降水)
2、核心功能方法
本次我没有直接在线请求 API(避免跨域、AK 泄露问题),而是把百度天气 API 拉取到的原始 JSON 数据单独拆分成多个 JS 文件,每个城市对应一个 weather-data-xxx.js,文件内挂载全局 WEATHER_DATA 对象,解耦性更强,后续更新数据只需要替换 JS 文件即可。核心 JS 逻辑主要分为数据解析 和页面渲染两大块,代码如下:
javascript
// Chart.js 通用配置
const tip = {
backgroundColor:'rgba(255,255,255,0.98)',
borderColor:'rgba(0,0,0,0.1)',borderWidth:1,
titleColor:'#1e293b',bodyColor:'#475569',
titleFont:{size:13,weight:'600'},
bodyFont:{size:12},
padding:10,cornerRadius:8,
displayColors:false
};
const xSL = { grid:{color:'rgba(0,0,0,0.04)'}, ticks:{color:'#94a3b8',font:{size:11}} };
const ySL = { grid:{color:'rgba(0,0,0,0.04)'}, ticks:{color:'#94a3b8',font:{size:11}} };
// AQI 0-500 → left% 映射(分段线性)
function aqiToPercent(aqi) {
// 优:0-50, 良:51-100, 轻:101-150, 中:151-200, 重:201-300, 严:301-500
return 85 + Math.min(((aqi - 300) / 200) * 15, 13);
}
// 周几简写
const WEEK_MAP = {'星期一':'周一','星期二':'周二','星期三':'周三',
'星期四':'周四','星期五':'周五','星期六':'周六','星期日':'周日'};
// 日期 YYYY-MM-DD → MM-DD
function fmtDate(d){ return d.slice(5); }
// 文字天气 + 夜晚 → 合并显示
function fmtWx(day, night){
if(day === night) return day;
return day + '<br>转' + night;
}
/* ── 主渲染函数 ── */
function render(d) {
const { meta, alerts, now, indexes, forecasts, forecast_hours: fh } = d;
/* ── 标题栏 ── */
document.title = meta.title + ' | 气象服务';
document.getElementById('tb-title').textContent = meta.title;;
/* ── 预警栏 ── */
const bar = document.getElementById('alert-bar');
if (alerts && alerts.length > 0) {
const a = alerts[0];
bar.className = 'alert-bar ' + (a.level === '红色' || a.level === '橙色' ? 'danger' : 'warn');
document.getElementById('alert-status').textContent = '【' + (a.type || '气象预警') + '】' + (a.level || '');;
} else {
bar.className = 'alert-bar safe';
document.getElementById('alert-status').textContent = '当前无气象预警';
document.getElementById('alert-status').style.color = 'var(--green)';
}
/* ── 实况 ── */
document.getElementById('now-temp').innerHTML =
now.temp + '<span class="now-unit">℃</span>';
/* ── AQI ── */
document.getElementById('aqi-num').textContent = now.aqi;
document.getElementById('aqi-tag').textContent = now.aqi_level + ' AQI';
document.getElementById('aqi-dot').style.left = aqiToPercent(now.aqi) + '%';
const pollItems = [
{ n: 'PM2.5', v: now.pm25 + ' μg/m³' },
{ n: 'PM10', v: now.pm10 + ' μg/m³' },
{ n: 'NO₂', v: now.no2 + ' μg/m³' },
{ n: 'SO₂', v: now.so2 + ' μg/m³' },
{ n: 'O₃', v: now.o3 + ' μg/m³' },
{ n: 'CO', v: now.co + ' mg/m³' }
];
document.getElementById('poll-grid').innerHTML = pollItems.map(p =>
'<div class="poll-item"><div class="poll-n">' + p.n + '</div>' +
'<div class="poll-v">' + p.v + '</div></div>'
).join('');
/* ── 7日预报 ── */
if (forecasts.length >= 2) {
document.getElementById('fc-range').textContent =
fmtDate(forecasts[0].date) + ' → ' + fmtDate(forecasts[forecasts.length - 1].date);
}
document.getElementById('forecast-row').innerHTML = forecasts.map(fc => {
const todayClass = fc.is_today ? ' today' : '';
const todayTag = fc.is_today ? '<div class="today-tag">今天</div>' : '';
return '<div class="fc-card' + todayClass + '">' +
todayTag +
'<div class="fc-wk">' + (WEEK_MAP[fc.week] || fc.week) + '</div>' +
'</div>';
}).join('');
/* ── 生活指数 ── */
document.getElementById('idx-list').innerHTML = indexes.map(idx =>
'<div class="idx-item">' +
'<div class="idx-ico">' + idx.icon + '</div>' +
'<div style="flex:1;">' +
'<div class="idx-name">' + idx.name + '</div>' +
'<div class="idx-brief ' + idx.level + '">' + idx.brief + '</div>' +
'</div>' +
'<div class="idx-detail" style="text-align:right;max-width:150px;">' + idx.detail + '</div>' +
'</div>'
).join('');
/* ── 图表数据准备 ── */
const fcLabels = forecasts.map(f => fmtDate(f.date));
const fcHigh = forecasts.map(f => f.high);
const fcLow = forecasts.map(f => f.low);
const hLabels = fh.map(h => h.label);
const hTemp = fh.map(h => h.temp_fc);
const hPop = fh.map(h => h.pop);
const pData = fh.map(h => h.prec_1h);
/* 7-day temp chart */
new Chart(document.getElementById('tempChart'),{
type:'line',
data:{ },
options:{ }
});
/* Hourly temp + pop */
new Chart(document.getElementById('hourlyChart'),{
type:'line',
data:{},
options:{});
/* Precipitation bars */
new Chart(document.getElementById('precChart'),{
type:'bar',
data:{},
options:{}
});
}
/* ── 数据加载入口 ──
* weather-data.js 通过 <script src> 加载为全局变量 window.WEATHER_DATA。
* 支持两种格式:
* A) 百度 API 原始格式:{ status:0, result:{ location, now, ... }, message }
* 更新数据时只需替换 weather-data.js 文件内容,程序自动识别格式。
*/
(function () {
var raw = window.WEATHER_DATA;
if (!raw) {
document.getElementById('loading').textContent =
'数据加载失败:weather-data.js 未正确加载,请检查文件是否存在。';
return;
}
var d;
// ── 格式 A:百度 API 原始格式 ──
if (raw.status === 0 && raw.result) {
var r = raw.result;
var loc = r.location || {};
var now = r.now || {};
// 将 uptime "20260602095000" → "2026-06-02 09:50"
var upRaw = String(now.uptime || '');
var uptime = upRaw.length >= 12
? upRaw.slice(0,4)+'-'+upRaw.slice(4,6)+'-'+upRaw.slice(6,8)
+' '+upRaw.slice(8,10)+':'+upRaw.slice(10,12)
: upRaw;
// 今日日期(YYYY-MM-DD)
var todayStr = (new Date()).toISOString().slice(0,10);
// 标记今日预报
var forecasts = (r.forecasts || []).map(function(fc) {
return Object.assign({}, fc, { is_today: fc.date === todayStr });
});
// 逐小时数据加上 label 字段(时间简写)
var forecast_hours = (r.forecast_hours || []).map(function(h) {
var t = h.data_time || '';
var label = t.length >= 16 ? t.slice(11,16) : t;
return Object.assign({}, h, { label: label });
});
// AQI 等级文字
var aqi = now.aqi || 0;
var aqiLevel = aqi <= 50 ? '优' : aqi <= 100 ? '良' : aqi <= 150 ? '轻度污染'
: aqi <= 200 ? '中度污染' : aqi <= 300 ? '重度污染' : '严重污染';
// UVI 描述
var uvi = now.uvi || 0;
var uviDesc = uvi <= 2 ? '最弱' : uvi <= 4 ? '弱' : uvi <= 6 ? '中等'
: uvi <= 7 ? '强' : uvi <= 10 ? '很强' : '极强';
// 天气文字 → emoji 图标映射
function wxIcon(text) {
if (!text) return '🌤';
if (text.indexOf('雷') >= 0) return '⛈';
return '🌤';
}
// 生活指数等级判断
function idxLevel(brief) {
var bad = ['不宜','不适宜','较不宜','差','较差','高发','很强','极强','较强','炎热'];
var mid = ['较适宜','一般','少发','弱','中等','适量'];
for (var i=0; i<bad.length; i++) if (brief.indexOf(bad[i])>=0) return 'bad';
for (var j=0; j<mid.length; j++) if (brief.indexOf(mid[j])>=0) return 'mid';
return 'good';
}
// 生活指数图标
var idxIconMap = {
'晨练指数':'🏃','洗车指数':'🚗','感冒指数':'🤧','紫外线指数':'☀️',
'穿衣指数':'👕','运动指数':'⚽','钓鱼指数':'🎣','旅游指数':'✈️',
'空气污染指数':'🌬','花粉指数':'🌸','舒适指数':'😊'
};
d = {
meta: {
title: loc.name + '天气预报',
location: loc,
uptime: uptime,
publish_time: uptime
},
alerts: r.alerts || [],
now: Object.assign({}, now, {
aqi_level: aqiLevel,
uvi_desc: uviDesc
}),
indexes: (r.indexes || []).map(function(idx) {
return Object.assign({}, idx, {
icon: idxIconMap[idx.name] || '📊',
level: idxLevel(idx.brief)
});
}),
forecasts: forecasts.map(function(fc) {
return Object.assign({}, fc, { icon: wxIcon(fc.text_day) });
}),
forecast_hours: forecast_hours
};
// ── 格式 B:旧版自定义格式 ──
} else if (raw.now && raw.forecasts) {
d = raw;
} else {
document.getElementById('loading').textContent =
'数据格式有误:无法识别 weather-data.js 中的数据结构,请检查格式。';
return;
}
render(d);
document.getElementById('loading').style.display = 'none';
document.getElementById('app').style.display = '';
})();
简单说下逻辑:
- 封装
render函数,专门解析百度 API 标准返回结构,提取城市、温度、天气、风力、七日预报、生活指数等关键字段; - 页面加载完成后,默认渲染对应城市的数据
3、数据获取与格式说明
本次所有天气数据,均来自百度地图天气 API ,接口返回格式是标准 JSON。 我按照湖南各地市行政区划编码,将数据拆分独立 JS 文件,每个文件中通过 window.WEATHER_DATA 挂载全局数据,优势很明显:

- 无需在线请求,彻底解决前端跨域问题;
- 数据和页面分离,后续需要更新天气,直接替换对应 JS 文件即可,不用改代码;
- 数据结构完全沿用百度原生格式,
now实时天气、forecasts七日预报、indexes生活指数字段齐全,拓展性很强。
关于如何集成百度地图到自己的应用,在之前的博客中进行了详细的介绍,大家可以根据需要翻阅之前的博文。
四、项目成果展示
结合我整理好的 JS 原始数据,下面逐一展示长沙、岳阳、怀化、郴州四座城市的解析结果,数据均为接口原始采集内容,参考价值很高。
1、长沙市

- 城市:湖南省长沙市
- 实时气温:26℃,体感温度:28℃
- 天气状况:轻雾,相对湿度:96%
- 风向风力:西风 1 级
- 当日预报:白天中雨 / 夜间中雨,气温 19℃ ~ 27℃
- 生活提示:建议穿短衫、短裤等清凉夏季。 另外长沙当前附带暴雨黄色预警,考生和家长出行一定要注意防范强降水。

2、岳阳市

- 城市:湖南省岳阳市
- 实时气温:26℃,体感温度:26℃
- 天气状况:轻雾,相对湿度:86%
- 风向风力:东北风 3 级
- 当日预报:白天小雨 / 夜间小雨,气温 15℃ ~ 29℃
- 生活提示:适合穿 T 恤、短薄外套等夏季服装。 岳阳今日有降雨,出门记得携带雨具。

3、怀化市

- 城市:湖南省怀化市
- 实时气温:23℃,体感温度:24℃
- 天气状况:小雨,相对湿度:97%
- 风向风力:东北风 2 级
- 当日预报:白天大雨 / 夜间中雨,气温 14℃ ~ 27℃
- 生活提示:适合穿 T 恤、短薄外套等夏季服装。 怀化目前降雨明显,路面湿滑,赶考务必提早出门。

4、郴州市

- 城市:湖南省郴州市
- 实时气温:25℃,体感温度:25℃
- 天气状况:多云,相对湿度:89%
- 风向风力:南风 3 级
- 当日预报:白天中雨 / 夜间中雨,气温 21℃ ~ 32℃
- 生活提示:建议穿长袖衬衫单裤等服装。 郴州湿度偏大,体感偏闷,注意通风降温。

整体来看,湖南近期普遍有降雨天气,大家一定要做好防雨准备。解锁其他更多城市,欢迎大家真实体验,也可以直接私信或在评论区中留言,博主提供截图回复。
五、总结
以上就是本文的主要内容。到这里,这个基于 HTML5 + 百度天气 API 数据的高考天气查询小项目就全部完成了。整个项目没有任何门槛,纯原生前端技术,非常适合前端新手用来练手 JSON 数据解析、DOM 动态渲染、静态数据分离 这几个核心知识点。从实用角度来说,它可以直接当作小型工具使用;从学习角度,也能帮大家理解第三方接口数据的解析思路。
后续如果想继续拓展功能,给大家几个方向:
- 完善城市列表,把湖南全部地市都加进来;
- 新增小时级天气预报、空气质量展示;
- 搭配天气图标、CSS 动画,优化页面视觉效果;
- 改成在线调用百度 API,实现实时自动更新。
最后也祝愿所有考生金榜题名,旗开得胜!