LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

使用 HTML + JavaScript 实现级联选择器(附完整代码)

admin
2025年12月25日 9:33 本文热度 1122
级联选择器是一种重要的用户界面组件,特别适用于处理具有层级关系的数据,如地区选择、分类选择等场景。通过级联选择器,用户可以从上到下逐级选择,每级选择都会影响下一级的选项内容,从而有效缩小选择范围,提升用户体验。本文将介绍如何使用 HTML、CSS 和 JavaScript 实现一个可配置的级联选择器。

效果演示

本系统实现了一个三级级联选择器,用户可以从省、市、区三个层级依次选择。

页面结构

页面主要包含以下区域:级联选择器容器(cascadeContainer)、选择信息展示区(selectedInfo)。
<div id="cascadeContainer" class="cascade-container"></div><div id="selectedInfo" class="selected-info"></div>

核心功能实现

渲染级联容器

renderCascade 函数根据配置的级数动态创建选择器容器,并初始化第一级数据。
function renderCascade(data) {  for (let i = 0; i < cascadeState.levels; i++) {    const levelDiv = document.createElement('div');    levelDiv.className = 'cascade-level';
    const wrapper = document.createElement('div');    wrapper.className = 'select-wrapper';
    const select = document.createElement('select');    select.id = `cascade-select-${i}`;    select.disabled = i > 0;
    const option = document.createElement('option');    option.value = '';    option.textContent = cascadeState.placeholder;    select.appendChild(option);    wrapper.appendChild(select);    levelDiv.appendChild(wrapper);
    cascadeContainer.appendChild(levelDiv);    cascadeState.selectElements.push(select);  }  // 初始化第一级数据  if (data.length > 0) {    populateSelect(0, data);    cascadeState.selectElements[0].disabled = false;  }}

绑定级联事件

bindCascadeEvents 函数为每个选择器绑定 change 事件,当用户选择某个选项时触发级联更新逻辑。事件处理函数会根据当前选择的层级和值,动态加载下一级选项。
function bindCascadeEvents() {  cascadeState.selectElements.forEach((select, index) => {    select.addEventListener('change'(e) => {      handleCascadeChange(index, e.target.value);    });  });}

处理级联变化

当用户在某级选择器选中值时,handleCascadeChange 会保存该值、清空并禁用后续所有级联选择器,若有值则立即加载下一级数据并刷新信息展示区。
function handleCascadeChange(level, value) {  cascadeState.selectedValues[level] = value;  // 清空后续选择  for (let i = level + 1; i < cascadeState.levels; i++) {    cascadeState.selectedValues[i] = '';    cascadeState.selectElements[i].innerHTML = `<option value="">${cascadeState.placeholder}</option>`;    cascadeState.selectElements[i].disabled = true;  }  if (value) {    loadNextLevel(level, value);  }  onCascadeSelect(cascadeState.selectedValues);  showSelectedInfo();}

加载下级数据

loadNextLevel 函数根据当前选择路径,从原始数据中找到对应的下级数据,并填充到下一个选择器中。该函数会逐级遍历数据结构,直到找到当前选择路径对应的下级选项。
function loadNextLevel(currentLevel) {  if (currentLevel >= cascadeState.levels - 1return;  const nextLevel = currentLevel + 1;  let currentData = regionData; // 使用全局数据  for (let i = 0; i <= currentLevel; i++) {    if (!cascadeState.selectedValues[i]) break;    const selectedData = findDataByValue(currentData, cascadeState.selectedValues[i]);    if (selectedData && selectedData[cascadeState.childrenKey]) {      currentData = selectedData[cascadeState.childrenKey];    } else {      return;    }  }  populateSelect(nextLevel, currentData);  cascadeState.selectElements[nextLevel].disabled = false;}

完整代码

git地址:https://gitee.com/ironpro/hjdemo/blob/master/select-cascade/index.html
<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>级联选择器</title>  <style>      * {          margin0;          padding0;          box-sizing: border-box;      }      body {          background#f5f5f5;          padding20px;          min-height100vh;          display: flex;          justify-content: center;          color#333;      }      .container {          background: white;          padding20px;          width800px;          height400px;          box-shadow0 2px 10px rgba(0,0,0,0.05);      }      h1 {          color#2c3e50;          font-size22px;          margin-bottom25px;          text-align: center;          border-bottom1px solid #eee;          padding-bottom15px;      }      .select-wrapper {          position: relative;          margin-bottom20px;      }      select {          width100%;          padding10px 14px;          border1px solid #ddd;          background: white;          font-size14px;          color#2c3e50;          cursor: pointer;          appearance: none;          outline: none;      }      select:hover {          border-color#888;      }      select:focus {          border-color#3498db;          box-shadow0 0 0 2px rgba(521522190.15);      }      .select-wrapper::after {          content'▼';          position: absolute;          right10px;          top50%;          transformtranslateY(-50%);          color#999;          font-size10px;          pointer-events: none;      }      select:disabled {          background#f8f9fa;          color#95a5a6;          cursor: not-allowed;      }      .cascade-container {          display: flex;          gap14px;          flex-wrap: wrap;      }      .cascade-level {          flex1;          min-width120px;      }      .selected-info {          margin-top20px;          padding14px;          background#e3f2fd;          font-size14px;          color#1976d2;          display: none;      }  </style></head><body><div class="container">  <h1>级联选择器</h1>  <div class="main">    <div id="cascadeContainer" class="cascade-container"></div>    <div id="selectedInfo" class="selected-info"></div>  </div></div>
<script>  const cascadeContainer = document.getElementById('cascadeContainer');  const selectedInfo = document.getElementById('selectedInfo');
  const cascadeState = {    containerId'cascadeContainer',    levels3,    placeholder'请选择',    valueKey'value',    labelKey'label',    childrenKey'children',    selectElements: [],    selectedValues: [],    defaultValue: [] // 新增:默认值配置  };  // 地区数据  const regionData = [    {      value"110000",      label"北京市",      children: [{        value"110100",        label"北京市",        children: [          { value"110101"label"东城区" },          { value"110102"label"西城区" },          { value"110105"label"朝阳区" },        ]      }]    },    // ...  ];  // 验证数据结构  function validateData(data) {    if (!Array.isArray(data)) return false;    for (const item of data) {      if (typeof item !== 'object' ||        !item.hasOwnProperty(cascadeState.valueKey) ||        !item.hasOwnProperty(cascadeState.labelKey)) {        return false;      }    }    return true;  }  // 渲染容器结构  function renderCascade(data) {    for (let i = 0; i < cascadeState.levels; i++) {      const levelDiv = document.createElement('div');      levelDiv.className = 'cascade-level';
      const wrapper = document.createElement('div');      wrapper.className = 'select-wrapper';
      const select = document.createElement('select');      select.id = `cascade-select-${i}`;      select.disabled = i > 0;
      const option = document.createElement('option');      option.value = '';      option.textContent = cascadeState.placeholder;      select.appendChild(option);      wrapper.appendChild(select);      levelDiv.appendChild(wrapper);
      cascadeContainer.appendChild(levelDiv);      cascadeState.selectElements.push(select);    }    // 初始化第一级数据    if (data.length > 0) {      populateSelect(0, data);      cascadeState.selectElements[0].disabled = false;    }  }  // 绑定事件  function bindCascadeEvents() {    cascadeState.selectElements.forEach((select, index) => {      select.addEventListener('change'(e) => {        handleCascadeChange(index, e.target.value);      });    });  }  // 处理选择变化  function handleCascadeChange(level, value) {    cascadeState.selectedValues[level] = value;    // 清空后续选择    for (let i = level + 1; i < cascadeState.levels; i++) {      cascadeState.selectedValues[i] = '';      cascadeState.selectElements[i].innerHTML = `<option value="">${cascadeState.placeholder}</option>`;      cascadeState.selectElements[i].disabled = true;    }    if (value) {      loadNextLevel(level, value);    }    onCascadeSelect(cascadeState.selectedValues);    showSelectedInfo();  }  // 加载下一级数据  function loadNextLevel(currentLevel) {    if (currentLevel >= cascadeState.levels - 1return;    const nextLevel = currentLevel + 1;    let currentData = regionData; // 使用全局数据    for (let i = 0; i <= currentLevel; i++) {      if (!cascadeState.selectedValues[i]) break;      const selectedData = findDataByValue(currentData, cascadeState.selectedValues[i]);      if (selectedData && selectedData[cascadeState.childrenKey]) {        currentData = selectedData[cascadeState.childrenKey];      } else {        return;      }    }    populateSelect(nextLevel, currentData);    cascadeState.selectElements[nextLevel].disabled = false;  }  // 填充选择器选项  function populateSelect(level, data) {    const select = cascadeState.selectElements[level];    select.innerHTML = `<option value="">${cascadeState.placeholder}</option>`;    if (!data || !Array.isArray(data)) return;    data.forEach(item => {      const option = document.createElement('option');      option.value = item[cascadeState.valueKey];      option.textContent = item[cascadeState.labelKey];      select.appendChild(option);    });  }  // 根据值查找数据项  function findDataByValue(data, value) {    if (!data || !Array.isArray(data)) return null;    return data.find(item => item[cascadeState.valueKey] === value) || null;  }  // 显示选择信息  function showSelectedInfo() {    const hasSelection = cascadeState.selectedValues.some(v => v);    if (hasSelection) {      selectedInfo.style.display = 'block';      selectedInfo.innerHTML = `<strong>已选择:</strong>${cascadeState.selectedValues.filter(v => v).join(' > ')}`;    } else {      selectedInfo.style.display = 'none';    }  }  // 设置默认值  function setDefaultValue(defaultValues) {    if (!Array.isArray(defaultValues) || defaultValues.length === 0return;    cascadeState.defaultValue = [...defaultValues];    for (let level = 0; level < defaultValues.length; level++) {      const value = defaultValues[level];      if (value && cascadeState.selectElements[level]) {        // 检查当前选项是否存在于当前级的数据中        const select = cascadeState.selectElements[level];        const optionExists = Array.from(select.options).some(option => option.value === value);        if (optionExists) {          select.value = value;          cascadeState.selectedValues[level] = value;          select.disabled = false;          if (level < defaultValues.length - 1) {            loadNextLevel(level);          }        } else {          break;        }      }    }    // 清空后续未设置默认值的层级    for (let level = defaultValues.length; level < cascadeState.levels; level++) {      if (cascadeState.selectElements[level]) {        cascadeState.selectElements[level].innerHTML = `<option value="">${cascadeState.placeholder}</option>`;        cascadeState.selectElements[level].disabled = true;        cascadeState.selectedValues[level] = '';      }    }    // 更新显示信息    showSelectedInfo();    onCascadeSelect(cascadeState.selectedValues);  }  // 初始化组件  function initCascade(data, defaultValues = []) {    if (!validateData(data)) {      console.error('数据结构不符合要求');      return;    }    renderCascade(data);    bindCascadeEvents();    // 设置默认值    if (Array.isArray(defaultValues) && defaultValues.length > 0) {      setDefaultValue(defaultValues);    }  }  // 获取选择值  function onCascadeSelect(values) {    console.log('选择值:', values);  }  // 初始化组件  initCascade(regionData);  // initCascade(regionData, ["440000", "440300", "440305"]);</script></body></html>


阅读原文:https://mp.weixin.qq.com/s/21_m4lX3BJeiglfetl5lEg


该文章在 2025/12/25 9:33:22 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved