前言
做后台表单的时候,经常会遇到省份、城市二级联动下拉框。选省份,城市自动刷新对应列表,没选省份的时候城市框提示 “请先选择省份”。
网上常见两种数据格式:
- 数组包对象格式:
[{province:'xx',city:[]}] - 对象格式:
{"省份名":[城市数组]}
本文用第二种对象格式来实现,代码简单易懂,适合新手学习,基于 jQuery 完成。
前提:页面需要引入 jQuery 库,否则 $ 符号会报错。
最终效果
- 页面加载完成,省份下拉渲染全部省份,默认提示:请选择省份
- 城市下拉初始提示:请先选择省份
- 切换省份,城市下拉自动更新为该省下所有城市
- 切回 “请选择省份”,城市下拉重置提示文字
HTML 结构
就两个 select 选择框,class 分别为province和city。
<!--省份下拉--> <select class="province"></select> <!--城市下拉--> <select class="city"> <option value="">请先选择省份</option> </select>JS 完整代码
数据用对象,key 是省份名称,value 是该省份下城市数组。
// 省市数据:对象格式 key=省份,value=城市数组 let city_data = { "广东省": [ "广州市", "深圳市", "珠海市", "汕头市", "佛山市", "韶关市", "湛江市", "肇庆市", "江门市", "茂名市" ], "浙江省": [ "杭州市", "宁波市", "温州市", "嘉兴市", "湖州市", "绍兴市", "金华市", "衢州市", "舟山市", "台州市" ], "江苏省": [ "南京市", "无锡市", "徐州市", "常州市", "苏州市", "南通市", "连云港市", "淮安市", "盐城市", "扬州市" ], "山东省": [ "济南市", "青岛市", "淄博市", "枣庄市", "东营市", "烟台市", "潍坊市", "济宁市", "泰安市", "威海市" ], "河南省": [ "郑州市", "开封市", "洛阳市", "平顶山市", "安阳市", "鹤壁市", "新乡市", "焦作市", "濮阳市", "许昌市" ] } $(document).ready(function () { //页面加载完毕,先渲染省份 renderProvince(city_data) }) /** * 渲染省份下拉框 * @param {Object} data 省市对象数据 */ function renderProvince(data) { let province_html = "" // 判断对象是否有属性 if (data && Object.keys(data).length > 0) { // 默认选项 province_html += `<option value="请选择省份">请选择省份</option>` // for...in遍历对象,拿到每一个省份key for (let key in data) { province_html += ` <option value="${key}">${key}</option> ` } //把拼接好的html塞进省份select $('.province').html(province_html) } } //省份下拉发生改变事件 $('.province').change(function () { //拿到选中的省份value let selected_province = $(this).val() renderCity(selected_province) }) /** * 根据选中省份渲染城市 * @param {String} data 选中的省份名称 */ function renderCity(data) { //直接对象取值,拿到对应省份的城市数组 let filterdata = city_data[data] let city_html = "" //判断城市数组存在并且长度大于0 if (filterdata && filterdata.length > 0) { for (let i = 0; i < filterdata.length; i++) { city_html += `<option value="${filterdata[i]}">${filterdata[i]}</option>` } $('.city').html(city_html) } else { // 没匹配到省份,重置提示 city_html = `<option value="">请先选择省份</option>` $('.city').html(city_html) } }代码拆解
1、数据格式说明
let city_data = { "河南省":["郑州市","新乡市"] }对象格式好处:根据省份名字直接city_data["河南省"]就能取出城市数组,不用循环 find 查找,写起来省事。 对比数组套对象的写法,少写一次 find 筛选,代码更简短。
2、renderProvince 渲染省份函数
Object.keys(data):拿到对象所有 key,判断对象不是空对象。for...in循环对象,把每一个省份拼接成<option>。- 使用
$('.province').html()直接覆盖下拉里面所有选项。
3、change 监听省份下拉改变
只要用户切换省份下拉,就触发 change 事件,获取当前选中省份的值,调用渲染城市的函数。
4、renderCity 渲染城市函数
city_data[data]:直接通过省份名称取出城市列表。- 如果取到城市数组,循环拼接城市 option 渲染。
- 如果没有取到(比如选中默认项 “请选择省份”),城市下拉重置提示 “请先选择省份”。
需要注意的坑
- 一定要引入 jQuery,不然
$()直接报错。 - 省份
value必须和对象的 key 完全一模一样,文字不能错字,否则拿不到城市数据。 html()方法是覆盖原有内容,会把 select 内部全部替换,所以每次都要完整拼接 option。- 当选中 “请选择省份” 时,
city_data["请选择省份"]结果是undefined,会走进 else 分支,城市框重置,刚好符合业务需求。
拓展
- 如果后端返回数组套对象格式
[{province:'',city:[]}],那就要用.find()去找对应省份。 - 如果要做三级联动(省市区),逻辑一模一样,多写一层渲染函数。
- 如果是从接口拿异步数据,要等接口返回之后再调用
renderProvince(),不能页面一加载就执行。
完整可运行 demo
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>二级下拉联动</title> <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script> </head> <body> <select class="province"></select> <select class="city"> <option value="">请先选择省份</option> </select> <script> let city_data = { "广东省": [ "广州市", "深圳市", "珠海市", "汕头市", "佛山市", "韶关市", "湛江市", "肇庆市", "江门市", "茂名市" ], "浙江省": [ "杭州市", "宁波市", "温州市", "嘉兴市", "湖州市", "绍兴市", "金华市", "衢州市", "舟山市", "台州市" ], "江苏省": [ "南京市", "无锡市", "徐州市", "常州市", "苏州市", "南通市", "连云港市", "淮安市", "盐城市", "扬州市" ], "山东省": [ "济南市", "青岛市", "淄博市", "枣庄市", "东营市", "烟台市", "潍坊市", "济宁市", "泰安市", "威海市" ], "河南省": [ "郑州市", "开封市", "洛阳市", "平顶山市", "安阳市", "鹤壁市", "新乡市", "焦作市", "濮阳市", "许昌市" ] } $(document).ready(function () { renderProvince(city_data) }) function renderProvince(data) { let province_html = "" if (data && Object.keys(data).length > 0) { province_html += `<option value="请选择省份">请选择省份</option>` for (let key in data) { province_html += `<option value="${key}">${key}</option>` } $('.province').html(province_html) } } $('.province').change(function () { let selected_province = $(this).val() renderCity(selected_province) }) function renderCity(data) { let filterdata = city_data[data] let city_html = "" if (filterdata && filterdata.length > 0) { for (let i = 0; i < filterdata.length; i++) { city_html += `<option value="${filterdata[i]}">${filterdata[i]}</option>` } $('.city').html(city_html) } else { city_html = `<option value="">请先选择省份</option>` $('.city').html(city_html) } } </script> </body> </html>复制全部代码保存 html 文件,直接浏览器打开就可以看到效果。
总结
二级联动核心逻辑就三步:
- 页面初始化渲染第一级下拉(省份)
- 监听第一级下拉 change 事件,拿到选中值
- 根据选中值过滤数据,渲染第二级下拉(城市)