文章目录▼CloseOpen
- 第一步:先搞清楚Ajax做三级联动的核心逻辑
- 第二步:跟着做!超详细的代码实现步骤
- 第三步:踩过的坑给你避避——这些细节别漏了
- Ajax做城市三级联动的核心逻辑到底是什么呀?
- 后端需要给我提供什么样的地址接口呀?
- 选省份后城市加载慢,或者没数据怎么办?
- 我是前端新手,写代码时容易犯什么错?
- 先写好HTML结构
这篇文章专门帮你解决这个痛点:从如何调用地址接口、处理异步数据返回,到实现“选省加载市、选市加载区”的联动逻辑,每一步都拆解得清清楚楚。不管你是前端新手还是想快速落地需求的开发者,跟着步骤走就能一步步实现。更贴心的是,文中附了可直接复制使用的完整代码——不用自己从头写,改改参数就能用到项目里,省掉大把调试时间。
不管是电商的收货地址、表单的地址选择,还是任何需要省市区联动的场景,这个功能都能直接用上。 咱们就手把手把“难搞”的三级联动变成“拿来就能用”的实用功能!
做网页表单的时候,你是不是经常碰到省市区三级联动的问题?选了省份,城市加载半天不出来;好不容易出来了,选城市又没反应——尤其是刚接触Ajax的新手,看着一堆代码头皮发麻?我去年帮朋友做美食店的外卖下单页时,也踩过这坑:一开始直接把所有地址数据写死在页面里,结果页面加载慢得要命,还占了好多空间;后来改成Ajax异步加载,才解决了问题,现在那个页面的地址选择流畅得很。今天就把我当时的实操步骤拆开来给你讲,连代码都给你准备好了,跟着做就行。
第一步:先搞清楚Ajax做三级联动的核心逻辑
其实Ajax就是帮你在不刷新页面的情况下,偷偷从服务器拿数据的工具。比如你选了“广东省”,Ajax就会悄悄告诉服务器“我要广东省的城市列表”,服务器把数据发回来,页面直接把城市填进下拉框里——全程不用刷新页面,所以才流畅。
三级联动的核心就是“触发-请求-渲染”循环:选省份(触发事件)→Ajax请求城市数据→渲染城市下拉框;选城市(触发事件)→Ajax请求区县数据→渲染区县下拉框。我当时一开始没搞懂这个逻辑,居然在选省份的时候同时请求了城市和区县的数据,结果数据乱成一团,后来才反应过来——得一步一步来,选一个层级再请求下一个层级的数据。
举个例子:你打开外卖下单页,首先看到省份下拉框里有“广东省”“湖南省”这些选项,这是页面加载时用Ajax从服务器拿的省份数据;你选了“广东省”,Ajax就去请求广东省的城市列表,拿到“广州市”“深圳市”这些数据,填进城市下拉框;你选了“广州市”,Ajax再去请求广州市的区县列表,填进区县下拉框——全程都是“选一个,拿一个”,所以数据不会乱,加载也快。
第二步:跟着做!超详细的代码实现步骤
你得把省市区三个下拉框的HTML写好。我当时写的结构很简单,就三个标签,分别加了ID,方便后面用JavaScript控制:
请选择省份
请选择城市
请选择区县
这里要注意:一开始城市和区县的下拉框要加disabled
属性——也就是禁用状态。不然用户没选省份就点城市,会出问题。我当时没加这个,朋友测试的时候乱点,结果页面报错了,后来赶紧加上的。
页面加载完成后,首先要把省份数据加载进来。我当时用了jQuery的Ajax方法(因为简单,新手容易上手),代码是这样的:
$(function() {
// 页面加载完成后,请求省份数据
$.ajax({
url: 'api/province/list', // 你的省份数据接口地址
type: 'GET', // 请求方式,一般用GET
dataType: 'json', // 告诉Ajax,服务器返回的是JSON格式数据
success: function(data) {
// 拿到数据后,渲染省份下拉框
var provinceSelect = $('#province');
// 遍历数据,把每个省份塞进下拉框
$.each(data, function(index, item) {
provinceSelect.append('' + item.name + '');
});
},
error: function() {
// 处理错误情况,比如服务器没响应
alert('省份数据加载失败,请重试');
}
});
});
这里的url
是你后端写的接口地址,比如你用PHP写了个接口api/province/list
,返回所有省份的JSON数据,Ajax就会去那里拿数据。success
函数是说,拿到数据后要做什么——这里就是把每个省份的id
和name
塞进省份下拉框里。error
函数是处理错误的,比如服务器宕机了,就弹个提示给用户,我去年没加这个,结果有次服务器维护,用户选省份没反应,差点以为页面坏了。
等省份数据加载好了,就得给省份下拉框加个“改变事件”——也就是用户选了某个省份之后,要触发Ajax请求城市数据。代码是这样的:
$('#province').change(function() {
// 拿到选中的省份ID
var provinceId = $(this).val();
// 如果选了省份(provinceId不为空)
if (provinceId) {
// 启用城市下拉框,清空之前的内容,加个“请选择城市”的选项
$('#city').prop('disabled', false).empty().append('请选择城市');
// 禁用区县下拉框,清空内容
$('#district').prop('disabled', true).empty().append('请选择区县');
// 请求城市数据
$.ajax({
url: 'api/city/list?province_id=' + provinceId, // 带省份ID的接口地址
type: 'GET',
dataType: 'json',
success: function(data) {
var citySelect = $('#city');
// 遍历城市数据,塞进城市下拉框
$.each(data, function(index, item) {
citySelect.append('' + item.name + '');
});
},
error: function() {
alert('城市数据加载失败,请重试');
}
});
} else {
// 如果没选省份,重置城市和区县下拉框
$('#city').prop('disabled', true).empty().append('请选择城市');
$('#district').prop('disabled', true).empty().append('请选择区县');
}
});
这里的关键是:选了省份之后,要先清空城市下拉框的旧数据,再启用它;同时把区县下拉框禁用并清空——不然之前的数据会留在里面,混淆用户。比如你先选了“广东省”,城市下拉框里有“广州市”,然后改选“湖南省”,要是不清空,城市下拉框里还会有“广州市”,就乱了。
城市选择事件的逻辑和省份差不多,就是请求区县数据。代码是这样的:
$('#city').change(function() {
// 拿到选中的城市ID
var cityId = $(this).val();
if (cityId) {
// 启用区县下拉框,清空内容
$('#district').prop('disabled', false).empty().append('请选择区县');
// 请求区县数据
$.ajax({
url: 'api/district/list?city_id=' + cityId, // 带城市ID的接口地址
type: 'GET',
dataType: 'json',
success: function(data) {
var districtSelect = $('#district');
// 遍历区县数据,塞进区县下拉框
$.each(data, function(index, item) {
districtSelect.append('' + item.name + '');
});
},
error: function() {
alert('区县数据加载失败,请重试');
}
});
} else {
// 没选城市,重置区县下拉框
$('#district').prop('disabled', true).empty().append('请选择区县');
}
});
到这里,核心代码就写完了。我把这些代码复制到朋友的外卖页里,测试了一下:选省份,城市秒加载;选城市,区县秒加载——比之前写死数据的方式流畅多了。
我把当时用的接口和参数整理成了表格,你直接对着找后端要接口就行:
层级 | 接口地址 | 请求参数 | 返回数据示例 |
---|---|---|---|
省份 | /api/province/list | 无 | [{“id”:1,”name”:”广东省”},…] |
城市 | /api/city/list | province_id(省份ID) | [{“id”:101,”name”:”广州市”},…] |
区县 | /api/district/list | city_id(城市ID) | [{“id”:10101,”name”:”天河区”},…] |
第三步:踩过的坑给你避避——这些细节别漏了
$.ajax
的beforeSend
函数里,给城市下拉框加加载中…
,不然用户以为没反应,会乱点。我当时没加这个,朋友测试的时候催了我三次“怎么还没出来?”,后来加上就好了。代码示例:$.ajax({
url: 'api/city/list?province_id=' + provinceId,
beforeSend: function() {
// 请求开始前,加“加载中…”选项
$('#city').empty().append('加载中…');
},
success: function(data) {
// 渲染城市数据
}
});
var cityCache = {}; // 缓存城市数据
$('#province').change(function() {
var provinceId = $(this).val();
if (provinceId) {
if (cityCache[provinceId]) {
// 从缓存里拿数据,不用请求服务器
renderCity(cityCache[provinceId]);
} else {
// 请求服务器,拿到数据后存进缓存
$.ajax({
url: 'api/city/list?province_id=' + provinceId,
success: function(data) {
cityCache[provinceId] = data;
renderCity(data);
}
});
}
}
});
// 专门渲染城市的函数
function renderCity(data) {
var citySelect = $('#city');
citySelect.empty().append('请选择城市');
$.each(data, function(index, item) {
citySelect.append('' + item.name + '');
});
}
这样一来,第二次选广东省的时候,直接从缓存里拿数据,加载更快。
success
函数里加了判断:success: function(data) {
var citySelect = $('#city');
citySelect.empty().append('请选择城市');
if (data.length === 0) {
// 没有数据,加个提示
citySelect.append('该省份暂无城市数据');
} else {
// 有数据,遍历渲染
$.each(data, function(index, item) {
citySelect.append('' + item.name + '');
});
}
}
这样就清楚多了。
按这些步骤做下来,你的三级联动应该就没问题了。我把完整的代码打包放在网盘里了,需要的话可以找我要——对了,你要是按这个方法试了,欢迎回来告诉我效果怎么样!比如有没有遇到什么奇怪的问题,我帮你看看。
Ajax做城市三级联动的核心逻辑到底是什么呀?
其实就是“选一个层级,请求下一个层级数据”的循环——比如你选了省份,Ajax就悄悄去服务器拿这个省份的城市列表,拿到后直接填进城市下拉框;等你选了城市,Ajax再去拿对应的区县数据。全程不用刷新页面,所以才流畅。我去年一开始没搞懂这个,居然选省份时同时请求了城市和区县数据,结果数据乱成一团,后来才明白得一步一步来。
后端需要给我提供什么样的地址接口呀?
得要三个层级的接口,分别返回省份、城市、区县数据。比如省份接口不用参数,直接返回所有省份的id和name;城市接口要传省份id,返回这个省份下的城市列表;区县接口要传城市id,返回对应区县列表。我整理过接口示例——比如省份接口是/api/province/list,返回[{“id”:1,”name”:”广东省”},…];城市接口是/api/city/list?province_id=1,返回广州市、深圳市这些数据;区县接口是/api/district/list?city_id=101,返回天河区、越秀区这类数据。
选省份后城市加载慢,或者没数据怎么办?
加载慢的话,可以加个loading状态——比如请求城市数据前,先给城市下拉框加个“加载中…”的选项,不然用户以为没反应。还可以加缓存,比如第一次拿到广东省的城市数据,存起来,下次再选广东省就不用再请求服务器了,直接用缓存里的。如果没数据,比如某个省份没有城市,要在下拉框里加“该省份暂无城市数据”的提示,别让用户空等。我去年没加loading,朋友测试时催了我三次“怎么还没出来”,后来加上就好了。
我是前端新手,写代码时容易犯什么错?
最容易犯的错就是把所有地址数据写死在页面里,结果页面加载慢得要命;或者选省份时同时请求城市和区县数据,导致数据混乱。还有没给城市、区县下拉框加disabled属性——比如用户没选省份就想点城市,这时候应该禁用城市下拉框,不然会报错。我一开始就没加disabled,朋友测试时乱点,页面直接报错了,后来赶紧加上的。 别忘处理空数据情况,比如拿到空数组时要提示用户,别让下拉框空着。