JavaScript 下拉列表 完全教程

下拉列表(在 HTML 中是 <select> 元素)是网页表单中最常用的交互元素之一,它允许用户从多个选项中进行选择,本教程将教你如何使用 JavaScript 来创建、操作和控制下拉列表。

javascript下拉列表教程
(图片来源网络,侵删)

目录

  1. 第一部分:基础操作

    • 1 获取下拉列表元素
    • 2 获取选中的值和文本
    • 3 动态添加选项
    • 4 动态删除选项
    • 5 清空所有选项
  2. 第二部分:进阶应用

    • 1 联动下拉列表(二级联动)
    • 2 搜索/过滤下拉列表
    • 3 创建多选下拉列表
    • 4 模拟一个自定义的下拉菜单
  3. 第三部分:最佳实践与注意事项

    • 1 使用 data-* 属性存储额外信息
    • 2 性能优化(大量数据时)
    • 3 可访问性 考虑

第一部分:基础操作

1 获取下拉列表元素

你需要获取页面上 <select> 元素的引用,最简单的方法是使用 document.getElementById()

javascript下拉列表教程
(图片来源网络,侵删)

HTML:

<label for="mySelect">选择一个水果:</label>
<select id="mySelect">
  <option value="">--请选择--</option>
  <option value="apple">苹果</option>
  <option value="banana">香蕉</option>
  <option value="orange">橙子</option>
</select>

JavaScript:

// 通过 ID 获取下拉列表元素
const selectElement = document.getElementById('mySelect');
console.log(selectElement); // 输出 <select> 元素本身

2 获取选中的值和文本

这是最常见的操作,当用户选择一个选项后,你需要知道他选择了什么。

  • selectElement.value: 获取当前选中选项的 value 属性值。
  • selectElement.options: 获取一个包含所有 <option> 元素的 HTMLOptionsCollection
  • selectElement.selectedIndex: 获取当前选中选项的索引。

示例:

javascript下拉列表教程
(图片来源网络,侵删)
const selectElement = document.getElementById('mySelect');
// 监听 change 事件,当选择改变时触发
selectElement.addEventListener('change', function() {
  // 获取选中的值
  const selectedValue = this.value;
  console.log('选中的值是:', selectedValue);
  // 获取选中的文本
  const selectedText = this.options[this.selectedIndex].text;
  console.log('选中的文本是:', selectedText);
});

运行结果:

  • 当你选择 "苹果" 时,控制台会输出:
    选中的值是: apple
    选中的文本是: 苹果
  • 当你选择 "香蕉" 时,控制台会输出:
    选中的值是: banana
    选中的文本是: 香蕉

3 动态添加选项

你可以使用 Option 构造函数或直接创建 <option> 元素来添加新选项。

使用 Option 构造函数 (推荐)

const selectElement = document.getElementById('mySelect');
// 创建一个新的 Option 对象
const newOption = new Option('葡萄', 'grape');
// 将新选项添加到下拉列表的末尾
selectElement.add(newOption);
// 如果想插入到指定位置,例如第二个位置(索引为1)
// selectElement.add(newOption, 1);

使用 DOM 方法

const selectElement = document.getElementById('mySelect');
// 1. 创建 <option> 元素
const optionElement = document.createElement('option');
// 2. 设置其属性
optionElement.value = 'strawberry';
optionElement.text = '草莓';
// 3. 将其添加到 <select> 中
selectElement.add(optionElement);

4 动态删除选项

使用 remove() 方法可以删除指定索引的选项。

const selectElement = document.getElementById('mySelect');
// 删除索引为 1 的选项 (即 "香蕉")
selectElement.remove(1);
// 也可以直接传一个 option 元素
// const optionToRemove = selectElement.options[2];
// selectElement.remove(optionToRemove);

5 清空所有选项

最简单的方法是将 selectElementlength 属性设置为 0。

const selectElement = document.getElementById('mySelect');
// 将 length 设置为 0 会清空所有选项
selectElement.length = 0;
// 也可以循环删除,但效率较低
// while (selectElement.options.length > 0) {
//   selectElement.remove(0);
// }

第二部分:进阶应用

1 联动下拉列表(二级联动)

这是一个非常经典的应用场景,例如选择省份后,城市列表自动更新。

HTML:

<label for="province">省份:</label>
<select id="province">
  <option value="">--请选择省份--</option>
  <option value="zj">浙江省</option>
  <option value="gd">广东省</option>
</select>
<label for="city">城市:</label>
<select id="city">
  <option value="">--请先选择省份--</option>
</select>

JavaScript:

const provinceSelect = document.getElementById('province');
const citySelect = document.getElementById('city');
// 城市数据
const cityData = {
  zj: ['杭州', '宁波', '温州'],
  gd: ['广州', '深圳', '珠海']
};
// 监听省份下拉列表的 change 事件
provinceSelect.addEventListener('change', function() {
  // 1. 清空城市下拉列表
  citySelect.length = 0;
  // 2. 获取选中的省份值
  const selectedProvince = this.value;
  // 3. 如果没有选择省份,则不进行操作
  if (!selectedProvince) {
    const defaultOption = new Option('--请先选择省份--', '');
    citySelect.add(defaultOption);
    return;
  }
  // 4. 遍历对应省份的城市数据
  const cities = cityData[selectedProvince];
  cities.forEach(cityName => {
    // 5. 为每个城市创建一个 option 并添加到城市列表
    const option = new Option(cityName, cityName);
    citySelect.add(option);
  });
});

2 搜索/过滤下拉列表

当选项很多时,提供一个搜索框来过滤选项会极大提升用户体验。

HTML:

<input type="text" id="searchInput" placeholder="搜索水果...">
<select id="filterableSelect" size="5"> <!-- size 属性可以显示更多选项 -->
  <option value="apple">Apple</option>
  <option value="apricot">Apricot</option>
  <option value="banana">Banana</option>
  <option value="blueberry">Blueberry</option>
  <option value="cherry">Cherry</option>
  <option value="grape">Grape</option>
  <option value="lemon">Lemon</option>
  <option value="mango">Mango</option>
</select>

JavaScript:

const searchInput = document.getElementById('searchInput');
const selectElement = document.getElementById('filterableSelect');
// 保存原始的所有选项
const originalOptions = Array.from(selectElement.options);
searchInput.addEventListener('input', function() {
  const searchTerm = this.value.toLowerCase();
  // 清空当前选项
  selectElement.length = 0;
  if (searchTerm === '') {
    // 如果搜索框为空,恢复所有原始选项
    originalOptions.forEach(option => {
      selectElement.add(option.cloneNode(true)); // cloneNode 避免引用同一对象
    });
  } else {
    // 过滤选项
    const filteredOptions = originalOptions.filter(option => {
      return option.text.toLowerCase().includes(searchTerm);
    });
    // 添加过滤后的选项
    if (filteredOptions.length > 0) {
      filteredOptions.forEach(option => {
        selectElement.add(option.cloneNode(true));
      });
    } else {
      // 如果没有匹配项,显示提示
      const noResultOption = new Option('没有找到匹配项', '');
      selectElement.add(noResultOption);
    }
  }
});

3 创建多选下拉列表

标准的 <select> 元素通过添加 multiple 属性可以实现多选,但用户体验不佳,下面我们用纯 JavaScript 和 CSS 模拟一个更美观的多选下拉框。

HTML:

<div class="custom-select" id="myCustomSelect">
  <div class="selected">请选择</div>
  <div class="options">
    <div class="option" data-value="apple">苹果</div>
    <div class="option" data-value="banana">香蕉</div>
    <div class="option" data-value="orange">橙子</div>
  </div>
</div>
<input type="hidden" id="selectedValues"> <!-- 用于存储选中的值 -->

CSS:

.custom-select {
  position: relative;
  width: 200px;
  user-select: none;
}
.selected {
  padding: 10px;
  border: 1px solid #ccc;
  cursor: pointer;
  background: white;
}
.options {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  border: 1px solid #ccc;
  border-top: none;
  background: white;
  display: none; /* 默认隐藏 */
  z-index: 10;
}
.options.show {
  display: block;
}
.option {
  padding: 10px;
  cursor: pointer;
}
.option:hover {
  background-color: #f0f0f0;
}
.option.selected {
  background-color: #e0e0e0;
}

JavaScript:

const customSelect = document.getElementById('myCustomSelect');
const selectedDiv = customSelect.querySelector('.selected');
const optionsDiv = customSelect.querySelector('.options');
const hiddenInput = document.getElementById('selectedValues');
// 点击 "selected" 区域来显示/隐藏选项
selectedDiv.addEventListener('click', function() {
  optionsDiv.classList.toggle('show');
});
// 点击选项
customSelect.querySelectorAll('.option').forEach(option => {
  option.addEventListener('click', function() {
    this.classList.toggle('selected'); // 切换选中状态样式
    // 收集所有选中的值
    const selectedValues = Array.from(customSelect.querySelectorAll('.option.selected'))
                                .map(opt => opt.dataset.value);
    // 更新隐藏输入框的值,方便表单提交
    hiddenInput.value = selectedValues.join(',');
    // 更新显示文本
    if (selectedValues.length > 0) {
      selectedDiv.textContent = `已选择 ${selectedValues.length} 项`;
    } else {
      selectedDiv.textContent = '请选择';
    }
  });
});
// 点击页面其他地方关闭下拉框
document.addEventListener('click', function(e) {
  if (!customSelect.contains(e.target)) {
    optionsDiv.classList.remove('show');
  }
});

4 模拟一个自定义的下拉菜单

这个例子与 2.3 类似,但更侧重于动态生成和事件委托,适用于选项非常多的情况。

<!-- 与 2.3 相同的 HTML 结构 -->
<div class="custom-select" id="dynamicSelect">
  <div class="selected">请选择</div>
  <div class="options"></div>
</div>
const dynamicSelect = document.getElementById('dynamicSelect');
const selectedDiv = dynamicSelect.querySelector('.selected');
const optionsDiv = dynamicSelect.querySelector('.options');
// 模拟从服务器获取的数据
const data = [
  { id: 1, name: '选项一' },
  { id: 2, name: '选项二' },
  { id: 3, name: '选项三' }
];
// 动态生成选项
data.forEach(item => {
  const optionDiv = document.createElement('div');
  optionDiv.className = 'option';
  optionDiv.dataset.value = item.id;
  optionDiv.textContent = item.name;
  optionsDiv.appendChild(optionDiv);
});
// 使用事件委托处理点击,比循环添加监听器更高效
optionsDiv.addEventListener('click', function(e) {
  // 确保点击的是 .option 元素
  if (e.target.classList.contains('option')) {
    // 这里可以添加单选或多选的逻辑
    // 单选示例:
    optionsDiv.querySelectorAll('.option').(opt => opt.classList.remove('selected'));
    e.target.classList.add('selected');
    selectedDiv.textContent = e.target.textContent;
    optionsDiv.classList.remove('show');
  }
});
// 点击显示/隐藏
selectedDiv.addEventListener('click', () => {
  optionsDiv.classList.toggle('show');
});

第三部分:最佳实践与注意事项

1 使用 data-* 属性存储额外信息

value 属性不足以满足需求,你可以使用自定义的 data-* 属性来存储任何你想在 JavaScript 中使用的信息。

HTML:

<select id="productSelect">
  <option value="prod_001" data-price="99.99" data-category="electronics">智能手机</option>
  <option value="prod_002" data-price="19.99" data-category="books">JavaScript高级程序设计</option>
</select>

JavaScript:

const productSelect = document.getElementById('productSelect');
productSelect.addEventListener('change', function() {
  const selectedOption = this.options[this.selectedIndex];
  const price = selectedOption.dataset.price;
  const category = selectedOption.dataset.category;
  console.log(`你选择了: ${selectedOption.text}`);
  console.log(`价格: $${price}`);
  console.log(`类别: ${category}`);
});

2 性能优化(大量数据时)

当下拉列表有成千上万个选项时,直接一次性渲染会导致页面卡顿。

  • 虚拟滚动: 只渲染可视区域内的选项,当用户滚动时,动态地添加和移除 DOM 元素,这需要更复杂的实现,但能极大提升性能。
  • 分步加载/懒加载: 初始只加载一部分选项,当用户滚动到底部时,通过 AJAX 请求加载更多数据并追加到列表中。

3 可访问性 考虑

当你用 <div><span> 模拟下拉列表时,可能会破坏屏幕阅读器等辅助工具的识别。

  • 使用正确的 ARIA 属性:
    • role="combobox": 表示一个组合框,通常是一个文本输入框和一个下拉列表。
    • role="listbox": 表示一个可选项的列表。
    • aria-expanded="true/false": 指示下拉列表当前是否展开。
    • aria-haspopup="listbox": 表明元素有一个弹出式的列表框。
    • aria-activedescendant: 指定当前激活的(通过键盘导航的)列表项的 ID。
  • 支持键盘导航: 确保用户可以使用 Tab 键聚焦,Enter 键打开/关闭,ArrowUp/ArrowDown 键在选项间导航,Escape 键关闭列表。
  • 保持 HTML 结构: 如果不需要复杂的自定义样式,尽量使用原生 <select><option> 标签,因为它们天生具有良好的可访问性。

本教程涵盖了从基础的获取、读写,到高级的联动、搜索和多选实现,并提供了性能和可访问性的建议,掌握这些技能,你将能够灵活地在任何项目中处理下拉列表相关的需求。

核心要点回顾:

  1. 使用 document.getElementById 或其他选择器获取 <select> 元素。
  2. 通过 valueoptions[index].text 获取选中值和文本。
  3. 使用 add()remove() 方法动态修改选项。
  4. 对于复杂交互,联动、搜索和多选是常见模式。
  5. 自定义组件时,务必考虑性能和可访问性。