You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JavaScript中运用DRY原则优化联动下拉框重复代码

优化联动下拉框的冗余代码,提升扩展性

我正在实现一组相互依赖的联动下拉框,构建下拉框的数据结构如下:

const data = {
    size: [ 
      { id: "s1", value: "009" },
      { id: "s2", value: "012" },
      { id: "s3", value: "041", configId: ["c2"], dischargeId: ["d3"] },
    ],
    config: [
      { id: "c1", value: "Horizontal", sizeId: ["s1", "s2"], dischargeId: ["d1", "d2"] },
      { id: "c2", value: "Vertical", dischargeId: ["d3"] },
    ],
     discharge: [
      {id: "d1", value: "S-Straight", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d2", value: "E-End", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d3", value: "T-Top", configId: ["c2"] },
    ]
}

部分数据对象带有额外属性,比如size中id为s3的对象包含configId和dischargeId属性,用来控制联动逻辑:

  • 当配置下拉框选中id为c1的选项时,size下拉框的s3选项需要禁用
  • 当排放下拉框选中d1或d2时,s3也需禁用
    其他属性遵循类似的联动逻辑。

目前代码功能正常,但存在大量重复逻辑,而且未来可能新增更多下拉项类型,希望优化代码,让它简洁低冗余,易于扩展。

当前实现代码如下:

const data = {
    size: [ 
      { id: "s1", value: "009" },
      { id: "s2", value: "012" },
      { id: "s3", value: "041", configId: ["c2"], dischargeId: ["d3"] },
    ],
    config: [
      { id: "c1", value: "Horizontal", sizeId: ["s1", "s2"], dischargeId: ["d1", "d2"] },
      { id: "c2", value: "Vertical", dischargeId: ["d3"] },
    ],
     discharge: [
      {id: "d1", value: "S-Straight", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d2", value: "E-End", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d3", value: "T-Top", configId: ["c2"] },
    ]
}

const sizeSelect = document.getElementById('size')
const configSelect = document.getElementById('configuration')
const dischargeSelect = document.getElementById('discharge')

const createSelect = (values, select) => {
  values.forEach(t => {
    let opt = document.createElement('option')
    opt.value = t.id
    opt.text = t.value
    select.append(opt)
  })
}

createSelect(data.size, sizeSelect)
createSelect(data.config, configSelect)
createSelect(data.discharge, dischargeSelect)

sizeSelect.addEventListener('change', (e) => {
  const val = e.target.value
  const size = data.size.find(t => t.id == val)
  if(size.configId){
  Array.from(configSelect.querySelectorAll('option')).forEach(o => o.disabled = true)
  size.configId.forEach(sId =>
    configSelect.querySelector(`option[value="${sId}"]`).disabled = false)
  }
  if(size.dischargeId){
  Array.from(dischargeSelect.querySelectorAll('option')).forEach(o => o.disabled = true)
  size.dischargeId.forEach(sId =>
    dischargeSelect.querySelector(`option[value="${sId}"]`).disabled = false)
  }
});

configSelect.addEventListener('change', (e) => {
  const val = e.target.value
  const config = data.config.find(s => s.id == val)
  if(config.sizeId){
    Array.from(configSelect.querySelectorAll('option')).forEach(o => o.disabled = false)
    Array.from(sizeSelect.querySelectorAll('option')).forEach(o => o.disabled = true);
    config.sizeId.forEach(sId =>
    sizeSelect.querySelector(`option[value="${sId}"]`).disabled = false)
  }
  if(config.dischargeId){
  Array.from(dischargeSelect.querySelectorAll('option')).forEach(o => o.disabled = true)
  config.dischargeId.forEach(sId =>
    dischargeSelect.querySelector(`option[value="${sId}"]`).disabled = false)
  }
});

dischargeSelect.addEventListener('change', (e) => {
  const val = e.target.value
  const discharge = data.discharge.find(s => s.id == val)
  if(discharge.sizeId){
    Array.from(sizeSelect.querySelectorAll('option')).forEach(o => o.disabled = true);
    discharge.sizeId.forEach(sId =>
    sizeSelect.querySelector(`option[value="${sId}"]`).disabled = false);
  }
  if(discharge.configId) {
    Array.from(configSelect.querySelectorAll('option')).forEach(o => o.disabled = true);
    discharge.configId.forEach(sId =>
    configSelect.querySelector(`option[value="${sId}"]`).disabled = false);
  }
});
<select id="size"></select>
<select id="configuration"></select>
<select id="discharge"></select>

优化方案

优化核心思路

  1. 统一DOM与数据映射:用对象管理所有下拉框元素和对应的数据集合,新增下拉项只需补充映射关系
  2. 提取通用函数:将创建选项、更新禁用状态、处理变更事件的逻辑封装成通用函数,消除重复代码
  3. 自动化联动规则:通过属性名后缀Id自动识别联动目标,无需为每个联动关系编写单独判断

优化后的完整代码

<select id="size"></select>
<select id="configuration"></select>
<select id="discharge"></select>

<script>
const data = {
    size: [ 
      { id: "s1", value: "009" },
      { id: "s2", value: "012" },
      { id: "s3", value: "041", configId: ["c2"], dischargeId: ["d3"] },
    ],
    config: [
      { id: "c1", value: "Horizontal", sizeId: ["s1", "s2"], dischargeId: ["d1", "d2"] },
      { id: "c2", value: "Vertical", dischargeId: ["d3"] },
    ],
     discharge: [
      {id: "d1", value: "S-Straight", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d2", value: "E-End", sizeId: ["s1", "s2"], configId: ["c1"] },
      {id: "d3", value: "T-Top", configId: ["c2"] },
    ]
};

// 下拉框key与DOM元素的映射,新增下拉项只需在此添加
const selectMap = {
  size: document.getElementById('size'),
  config: document.getElementById('configuration'),
  discharge: document.getElementById('discharge')
};

// 通用创建下拉框选项函数
function createOptions(dataKey) {
  const select = selectMap[dataKey];
  data[dataKey].forEach(item => {
    const opt = document.createElement('option');
    opt.value = item.id;
    opt.text = item.value;
    select.appendChild(opt);
  });
}

// 通用更新下拉框禁用状态函数
function updateSelectEnabled(targetKey, allowedIds) {
  const targetSelect = selectMap[targetKey];
  // 先禁用所有选项
  Array.from(targetSelect.options).forEach(opt => opt.disabled = true);
  // 启用允许的选项
  allowedIds.forEach(id => {
    const opt = targetSelect.querySelector(`option[value="${id}"]`);
    if (opt) opt.disabled = false;
  });
}

// 通用下拉框变更事件处理函数
function handleSelectChange(dataKey) {
  return function(e) {
    const selectedId = e.target.value;
    const selectedItem = data[dataKey].find(item => item.id === selectedId);
    if (!selectedItem) return;

    // 遍历当前选中项的所有属性,自动识别联动规则
    Object.keys(selectedItem).forEach(key => {
      if (key.endsWith('Id')) {
        const targetKey = key.slice(0, -2); // 从configId提取config
        if (selectMap[targetKey]) {
          updateSelectEnabled(targetKey, selectedItem[key]);
        }
      }
    });
  };
}

// 初始化所有下拉框
Object.keys(selectMap).forEach(key => {
  createOptions(key);
  selectMap[key].addEventListener('change', handleSelectChange(key));
});
</script>

优化效果说明

  • 高扩展性:新增下拉项时,只需在data中添加对应数据集合,在selectMap中补充DOM元素映射,核心逻辑无需修改
  • 低冗余:所有重复的DOM操作、联动逻辑都被封装成通用函数,代码量大幅减少
  • 易维护:联动规则通过属性名自动识别,逻辑清晰,后续修改或新增联动关系更便捷

内容的提问来源于stack exchange,提问作者Hello World

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:25:31