当前位置:首页 > 综合

js二级联动下拉菜单

做后台管理系统的动下单时候,省市区选择、拉菜商品分类筛选、动下单部门人员分配,拉菜这些场景都绕不开二级联动下拉菜单。动下单说白了就是拉菜选完第一个下拉框,第二个下拉框的动下单内容跟着变。逻辑不复杂,拉菜但真正写起来,动下单细节挺多。拉菜

最直接的动下单实现方式:准备两份数据。一份是拉菜省份列表,一份是动下单城市列表,城市数据里带一个关联字段指向省份。拉菜页面加载时先渲染省份下拉框,动下单监听它的change事件,用户选了某个省,就去城市数据里过滤出对应的城市,重新渲染城市下拉框。

先看数据结构怎么设计。假设用数组存:

const provinces = [ {  id: 1, name: '广东' }, {  id: 2, name: '浙江' }, {  id: 3, name: '四川' } ]; const cities = [ {  id: 101, provinceId: 1, name: '广州' }, {  id: 102, provinceId: 1, name: '深圳' }, {  id: 103, provinceId: 1, name: '东莞' }, {  id: 201, provinceId: 2, name: '杭州' }, {  id: 202, provinceId: 2, name: '宁波' }, {  id: 301, provinceId: 3, name: '成都' }, {  id: 302, provinceId: 3, name: '绵阳' } ];

这种扁平结构比嵌套结构好用。过滤的时候一行代码搞定:cities.filter(c => c.provinceId === selectedProvinceId)。如果用嵌套对象,每次增删改数据都得考虑层级关系,维护成本反而高。

HTML部分很简单,两个select标签,给上id方便JS操作:

<select id="province"> <option value="">请选择省份</option> </select> <select id="city"> <option value="">请选择城市</option> </select>

JS初始化阶段,把省份数据填进去。这里有个容易忽略的点——默认项。很多教程只渲染数据项,忘了加一个“请选择”的空选项。用户如果先选了省份再想清空,没有默认项就没法回到初始状态。

const provinceSelect = document.getElementById('province'); const citySelect = document.getElementById('city'); provinces.forEach(p => {  const opt = document.createElement('option'); opt.value = p.id; opt.textContent = p.name; provinceSelect.appendChild(opt); });

联动逻辑写在change事件里:

provinceSelect.addEventListener('change', function() {  const pid = parseInt(this.value); citySelect.innerHTML = '<option value="">请选择城市</option>'; if (!pid) return; const filtered = cities.filter(c => c.provinceId === pid); filtered.forEach(c => {  const opt = document.createElement('option'); opt.value = c.id; opt.textContent = c.name; citySelect.appendChild(opt); }); });

注意citySelect.innerHTML = '...'这行。每次切换省份,必须先把城市下拉框清空,否则新城市会追加到旧数据后面。我第一次写的时候就踩了这个坑——切了三个省,城市列表里堆了十几个选项,排查了半天才发现是没清空。

还有个细节:this.value拿到的是字符串,用parseInt转成数字再比较。如果数据里的id是字符串类型,那就不用转,但两边类型得一致。用===比较的时候,类型不匹配会直接返回false,下拉框看起来没反应,实际是过滤条件没生效。

用innerHTML重建选项在数据量小的时候没问题。城市超过几百个,频繁操作DOM会卡。这种情况可以用documentFragment先组装好再一次性插入,或者干脆把城市数据按省份分组缓存起来,切换时直接取缓存,不重复过滤。

另外,如果页面需要回显——比如编辑用户信息时已经存了省份和城市ID——那初始化的时候得先设置省份的值,手动触发一次change事件,再设置城市的值。顺序反了城市下拉框就是空的。这个逻辑用代码写出来大概是这样:

provinceSelect.value = savedProvinceId; provinceSelect.dispatchEvent(new Event('change')); citySelect.value = savedCityId;

实际项目里我倾向于把这段逻辑封装成一个函数,传入两个select元素的id和两份数据,返回一个初始化方法。这样省市区三级联动、分类二级联动都能复用同一套代码,不用每次重写。封装的时候把清空、过滤、渲染、回显这几个步骤拆开,哪个环节出问题一眼就能看出来。

二级联动本身没什么高深的技术,核心就是“监听变化 → 过滤数据 → 重建选项”这三步。但每一步都有坑,数据格式选错了后面越写越别扭,清空逻辑漏了会出现脏数据,类型比较不严谨会导致静默失败。把这些细节处理好,代码就稳了。

分享到: