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

如何用Select2实现基于字符串的三级动态链式选择

实现三级联动的Select2职业选择器(DnD计算器)

问题背景

开发DnD风格计算器时,需要三个Select2选择框分别展示一级、二级、三级职业选项,选项需根据上一级的选择动态生成。现有职业数组结构,但尝试用find()仅能返回第一个匹配项,filter()则返回undefined,且不想通过拆分数组加parent键的方式实现(维护成本高)。

关键问题修正

首先注意原职业数组的结构错误:每个一级职业的children被写成了单个对象,其中重复的id会导致后项覆盖前项(比如fighter的children中soldier会覆盖barbarian)。正确的结构应该是children为数组,每个子职业是独立对象。

解决方案

修正后的职业数组

const classes1 = [
    { id: '', text: 'Select ICQ...' },
    {
        id: 'fighter',
        text: 'Fighter',
        children: [
            {
                id: 'barbarian',
                text: 'Barbarian',
                children: [{ id: 'beserker', text: 'Beserker' }]
            },
            {
                id: 'soldier',
                text: 'Soldier',
                children: [{ id: 'knight', text: 'Knight' }]
            }
        ]
    },
    {
        id: 'archer',
        text: 'Archer',
        children: [
            {
                id: 'ranger',
                text: 'Ranger',
                children: [{ id: 'elite-ranger', text: 'Elite Ranger' }]
            },
            {
                id: 'paladin',
                text: 'Paladin',
                children: [{ id: 'bard', text: 'Bard' }]
            }
        ]
    },
    {
        id: 'mage',
        text: 'Mage',
        children: [
            {
                id: 'wizard',
                text: 'Wizard',
                children: [{ id: 'warlock', text: 'Warlock' }]
            },
            {
                id: 'druid',
                text: 'Druid',
                children: [{ id: 'artificer', text: 'Artificer' }]
            }
        ]
    }
];

完整实现代码

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

<div id="classes" class="form-int">
  <h5>Classes</h5>
  <select id="classtree1" class="frm-select" name="class-1"></select>
  <select id="classtree2" class="frm-select" name="class-2"></select>
  <select id="classtree3" class="frm-select" name="class-3"></select>
  <div class="row">
    <div class="box">
      <h6>Class1</h6>
      <span id="test1"></span>
    </div>
    <div class="box">
      <h6>Class2</h6>
      <span id="test2"></span>
    </div>
    <div class="box">
      <h6>Class3</h6>
      <span id="test3"></span>
    </div>
  </div>
</div>

CSS

#classes select {
  width:30%;
  margin-right: 10px;
}

JavaScript

// 初始化所有Select2组件
function initSelect2(selector, data, placeholder) {
  $(selector).empty().select2({
    multiple: false,
    placeholder: placeholder || "Select...",
    data: data,
    allowClear: true
  });
}

// 初始化一级选择框
initSelect2('#classtree1', classes1, 'Select class...');

// 一级选择框联动逻辑
$("#classtree1").on("change", function () {
  const selectedId = $(this).val();
  $('#test1').text(selectedId);
  
  // 清空二级、三级选择框
  initSelect2('#classtree2', [{id: '', text: 'Select subclass...'}]);
  initSelect2('#classtree3', [{id: '', text: 'Select specialization...'}]);
  $('#test2').text('');
  $('#test3').text('');

  if (!selectedId) return;
  
  // 找到选中的一级职业,获取其子项作为二级数据源
  const selectedClass = classes1.find(item => item.id === selectedId);
  if (selectedClass?.children) {
    // 添加默认项
    const secondLevelData = [{id: '', text: 'Select subclass...'}, ...selectedClass.children];
    initSelect2('#classtree2', secondLevelData);
  }
});

// 二级选择框联动逻辑
$("#classtree2").on("change", function () {
  const selectedId = $(this).val();
  $('#test2').text(selectedId);
  
  // 清空三级选择框
  initSelect2('#classtree3', [{id: '', text: 'Select specialization...'}]);
  $('#test3').text('');

  if (!selectedId) return;
  
  // 遍历所有一级职业的子项,找到选中的二级职业
  let selectedSubclass = null;
  for (const cls of classes1) {
    if (cls.children) {
      selectedSubclass = cls.children.find(item => item.id === selectedId);
      if (selectedSubclass) break;
    }
  }
  
  if (selectedSubclass?.children) {
    // 添加默认项
    const thirdLevelData = [{id: '', text: 'Select specialization...'}, ...selectedSubclass.children];
    initSelect2('#classtree3', thirdLevelData);
  }
});

// 三级选择框变化监听
$("#classtree3").on("change", function () {
  const selectedId = $(this).val();
  $('#test3').text(selectedId);
});

逻辑说明

  1. 初始化封装:用initSelect2函数统一处理Select2的初始化,避免重复代码,同时支持清空旧选项、设置新数据源。
  2. 一级联动:选中一级职业后,通过find()找到对应项,提取其children作为二级选择框的数据源,同时重置二级、三级选择框。
  3. 二级联动:遍历所有一级职业的子项,找到选中的二级职业,提取其children作为三级选择框的数据源。
  4. 默认项处理:每个选择框都添加空默认项,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:10:10