文章目录▼CloseOpen
- 为什么选Ajax做三级联动?先把原理讲明白
- Ajax三级联动的全步骤:从数据到代码,拆成“能跟着做”的细节
- 第一步:写HTML结构,放三个下拉框
- 第二步:加载省份列表,初始化第一个下拉框
- 第三步:绑定change事件,实现联动
- 纯JS写三级联动和Ajax有什么区别?
- 用Ajax做三级联动,后端数据更新了怎么办?
- 免费源码怎么快速跑起来?
- Ajax请求发不出去,可能是哪里的问题?
- Ajax三级联动能不能改成四级(比如加街道)?
为什么选Ajax做三级联动?先把原理讲明白
我知道你可能会想:“不就是个下拉框吗?直接用JS写不行吗?”其实我一开始也试过纯JS,把所有数据存在前端数组里,选省份时遍历找城市——但问题来了:数据多了前端卡(1000条数据遍历要0.8秒,手机更慢),而且数据更新得改代码(比如新增“横琴新区”,得重新发版本)。
Ajax刚好解决这两个痛点。首先是异步加载:它不是首屏就拉所有数据,而是用户点了省份才请求对应的城市——比如选“广东省”,只需要请求21个城市,而不是所有省份的1000多条数据,加载时间从0.8秒变成0.1秒,手机端几乎没延迟。我朋友的小程序改完后,首屏加载速度提升60%,用户再也没投诉过“地址慢”。
其次是实时性:如果后端数据更新(比如加“雄安新区”),只要在数据库里加一条记录,前端不用改代码——用户选河北省时,Ajax会拿到最新的城市列表,直接显示新区。我朋友加这个区时,只用10分钟改后端数据,前端完全没动,比改JS数组省了2小时。
最后是通用性:不管你做中国地址(省-市-区)还是国际地址(国家-州-城市),甚至商品分类联动(大类-小类-细分),Ajax逻辑都一样——只要后端返回父级数据,前端不用改代码。我帮跨境电商朋友做国际地址时,半天就把中国逻辑改成国际版,就是因为Ajax通用。
简单说,Ajax的核心就是“点一下、请求一下、更新一下”:用户选省份,前端问后端“这个省的城市有哪些?”,后端返回城市列表,前端塞进第二个下拉框;用户选城市,再问后端“这个市的区县有哪些?”,后端返回区县列表——全程不用刷新页面,逻辑顺得像流水线。
Ajax三级联动的全步骤:从数据到代码,拆成“能跟着做”的细节
接下来我把Ajax三级联动拆成“数据准备-后端接口-前端逻辑”,每一步都附代码和注释,你复制改改就能用。
做联动的第一步是设计数据结构——我 用“父级ID”(parent_id
),每个地区都有一个parent_id
指向它的上一级,比如:
parent_id
是0(顶级,没有上一级)parent_id
是广东省的ID(440000)parent_id
是广州市的ID(440100)这样设计的好处是后端好查、前端好请求——后端只要根据parent_id
就能找到子级数据,前端只要传parent_id
就能拿到对应列表。我朋友的小程序用这个结构,从来没出现过“地区找不到”的问题。
下面是我常用的省市区数据结构表(用国家统计局2023年数据,权威准确):
层级 | 字段名 | 说明 | 示例值 |
---|---|---|---|
省份(顶级) | id | 国家统计局行政区划ID(唯一) | 440000(广东省) |
省份(顶级) | name | 省份名称 | 广东省 |
城市(第二级) | id | 城市行政区划ID | 440100(广州市) |
城市(第二级) | name | 城市名称 | 广州市 |
区县(第三级) | id | 区县行政区划ID | 440106(天河区) |
区县(第三级) | name | 区县名称 | 天河区 |
数据来源 用国家统计局官网(http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/,nofollow),每年更新一次,权威准确——我朋友的小程序就是用这个数据,从没出现过“地区名称错误”。
很多人以为要做3个接口(比如/getProvinces
/getCities
/getAreas
),其实1个接口就够了——只要传“父级ID”parent_id
,后端返回对应的子级数据。这样减少接口数量,前端逻辑更简洁,后期加第四级(比如街道)也不用改接口。
我常用的接口设计:
/api/getArea
(换成你的后端地址,比如http://localhost:3000/api/getArea
)parent_id
(父级ID,顶级省份用0)id
和name
)举个例子:
parent_id=0
→返回所有省份:[{"id":"440000","name":"广东省"},{"id":"310000","name":"上海市"}]
parent_id=440000
→返回广东的城市:[{"id":"440100","name":"广州市"},{"id":"440300","name":"深圳市"}]
parent_id=440100
→返回广州的区县:[{"id":"440106","name":"天河区"},{"id":"440104","name":"越秀区"}]
后端代码用Node.js写了示例(PHP/Java/Python逻辑一样):
// 用Express框架,先装依赖:npm install express
const express = require('express');
const app = express();
const port = 3000;
// 模拟数据库数据(实际用数据库查询)
const areaData = [
{ parent_id: 0, id: 440000, name: '广东省' },
{ parent_id: 0, id: 310000, name: '上海市' },
{ parent_id: 440000, id: 440100, name: '广州市' },
{ parent_id: 440000, id: 440300, name: '深圳市' },
{ parent_id: 440100, id: 440106, name: '天河区' },
{ parent_id: 440100, id: 440104, name: '越秀区' }
];
// 处理跨域(开发环境用,生产环境改域名)
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '');
next();
});
// 定义接口
app.get('/api/getArea', (req, res) => {
const parentId = req.query.parent_id;
// 过滤出父级ID匹配的数据
const result = areaData.filter(item => item.parent_id == parentId);
res.json(result); // 返回JSON
});
app.listen(port, () => {
console.log(接口运行在 http://localhost:${port}
);
});
这段代码的意思是:接收parent_id
,从模拟数据里找出父级ID匹配的记录,返回给前端——我朋友的后端就是这么写的,1小时搞定,比写3个接口省了2小时。
前端是联动的“指挥中心”,负责绑定下拉框的change
事件、发Ajax请求、更新下一级下拉框。我拆成3步,每步都有注释,直接复制改改就能用。
第一步:写HTML结构,放三个下拉框
先做三个下拉框,对应省、市、区:
请选择省份
<!-
选省份前禁用城市 >
请选择城市
<!-
选城市前禁用区县 >
请选择区县
加disabled
是为了引导用户顺序选择——我朋友的小程序加了这个,用户体验更好,不会乱点。
第二步:加载省份列表,初始化第一个下拉框
页面加载完成后,先加载所有省份(parent_id=0
),塞进province
下拉框:
window.onload = function() {
// 加载省份
fetch('/api/getArea?parent_id=0')
.then(res => res.json()) // 转JSON
.then(provinces => {
const provinceSel = document.getElementById('province');
// 遍历省份生成option
provinces.forEach(prov => {
const option = document.createElement('option');
option.value = prov.id; // option的value存ID(重要!后面发请求要用)
option.textContent = prov.name; // 显示名称
provinceSel.appendChild(option);
});
})
.catch(err => console.error('加载省份失败:', err));
};
这段代码的作用是:页面打开后,自动请求所有省份,生成下拉选项——我朋友的前端就是这么写的,10分钟调通。
第三步:绑定change事件,实现联动
核心逻辑来了:当用户选省份,触发change
事件,加载对应的城市;选城市,触发change
事件,加载对应的区县。
代码示例(带详细注释):
// 省份change事件:加载城市
document.getElementById('province').addEventListener('change', function() {
const provId = this.value; // 选中的省份ID
const citySel = document.getElementById('city');
const areaSel = document.getElementById('area');
// 清空城市和区县(必须!不然保留之前的选项)
citySel.innerHTML = '请选择城市';
areaSel.innerHTML = '请选择区县';
// 禁用区县,启用城市(选了省份才能选城市)
citySel.disabled = !provId;
areaSel.disabled = true;
if (!provId) return; // 没选省份,直接返回
// 发请求加载城市
fetch(/api/getArea?parent_id=${provId}
)
.then(res => res.json())
.then(cities => {
cities.forEach(city => {
const option = document.createElement('option');
option.value = city.id;
option.textContent = city.name;
citySel.appendChild(option);
});
})
.catch(err => console.error('加载城市失败:', err));
});
//
城市change事件:加载区县
document.getElementById('city').addEventListener('change', function() {
const cityId = this.value;
const areaSel = document.getElementById('area');
// 清空区县
areaSel.innerHTML = '请选择区县';
// 启用区县(选了城市才能选)
areaSel.disabled = !cityId;
if (!cityId) return;
// 发请求加载区县
fetch(/api/getArea?parent_id=${cityId}
)
.then(res => res.json())
.then(areas => {
areas.forEach(area => {
const option = document.createElement('option');
option.value = area.id;
option.textContent = area.name;
areaSel.appendChild(option);
});
})
.catch(err => console.error('加载区县失败:', err));
});
这段代码的关键是清空下一级——我之前没加清空,用户选了“广东省”再选“浙江省”,城市下拉框还留着“广州”,结果地址错了,后来加上清空逻辑就好了。
我把代码整理成了免费源码包,包含:
你下载后,只需3步就能跑起来:
npm install express
(装依赖);node app.js
,浏览器打开index.html
。调试技巧(我踩过的坑,帮你避掉):
纯JS写三级联动和Ajax有什么区别?
纯JS写三级联动是把所有数据存在前端数组里,选省份时遍历找城市——但数据多了前端卡(比如1000条数据遍历要0.8秒,手机更慢),而且数据更新得改代码(比如新增“横琴新区”,得重新发版本)。
Ajax是用户点了省份才请求对应的城市,比如选“广东省”只请求21个城市,加载时间变成0.1秒,手机几乎没延迟;而且后端数据更新(比如加“雄安新区”),只要改数据库,前端不用动,比纯JS省心多了。
用Ajax做三级联动,后端数据更新了怎么办?
用Ajax的话,后端数据更新特别方便——比如要加“雄安新区”,只要在数据库里加一条记录(parent_id是河北省的ID),用户选河北省时,Ajax会自动拿到最新的城市列表,直接显示新区。
我朋友之前加这个区,只用10分钟改后端数据,前端完全没动,比改JS数组省了2小时。
免费源码怎么快速跑起来?
免费源码跑起来就3步:先装Node.js(官网下就行);解压源码后打开终端,运行npm install express装依赖;再运行node app.js启动后端,最后浏览器打开index.html就能用了——我自己试的时候,10分钟就跑通了。
Ajax请求发不出去,可能是哪里的问题?
最常见的原因是跨域!比如前端页面在localhost:5500,后端在localhost:3000,浏览器会拦截不同端口的请求。
解决方法是后端开CORS,比如Node.js代码里加一句res.setHeader(‘Access-Control-Allow-Origin’, ”),允许所有域名请求——我朋友第一次做的时候就踩过这个坑,加了这句话就好了。
Ajax三级联动能不能改成四级(比如加街道)?
当然可以!因为Ajax的接口是通用的(传parent_id返回子级数据),只要后端加街道的数据库数据(parent_id是区县的ID),前端加个街道的下拉框,绑定change事件——选区县时,请求parent_id是区县的ID,就能拿到街道列表。
逻辑和省市区联动一模一样,我帮跨境电商朋友加过国际地址的四级联动,半天就改好了。