选择框编程
选择框编程
选择框是通过 <select> 和 <option> 元素创建的。它常用于在有限选项里挑选值、做条件过滤或配置参数。为了方便与这个控件交互,除了所有表单字段共有的属性和方法外,HTMLSelectElement 类型还提供下列属性和方法。
常见使用场景
- 让用户从枚举/字典数据中选择(国家、省份、状态、角色等)
- 构建左右双列表等多选交互,批量移动或排序选项
- 配合搜索框实现动态过滤或联动选择(省市、类别/子类别)
- 从服务器动态加载选项,例如根据用户输入即时返回候选项
设计数据结构时,通常会将 value 作为提交给后端的稳定标识,text 为对用户友好的展示文案,两者应保持同步更新。
接口速览
| 成员 | 类型/返回值 | 说明 |
|---|---|---|
add(option, rel) | 方法 | 在参考项 rel 之前插入新 <option>;传入 null/undefined 时追加到末尾 |
length | 数字 | 选项数量,可读写 |
multiple | 布尔值 | 是否允许多选,对应 HTML 的 multiple 属性 |
options | HTMLOptionsCollection | 所有 <option> 的集合,可通过索引或名称访问 |
remove(index) | 方法 | 移除指定位置的选项 |
selectedIndex | 数字 | 第一个被选中的选项下标,无选中项时为 -1 |
selectedOptions | HTMLCollection | 只读集合,包含当前所有选中的 <option>(多选时尤其方便) |
size | 数字 | 可见的行数,对应 HTML 的 size 属性 |
type | 字符串 | select-one 或 select-multiple |
value | 字符串 | 当前选中项的值,等价于 HTML 中的 value 属性 |
选择框的 value 属性由当前选中项决定,相应规则如下:
- 若没有选中项,则返回空字符串
- 若存在单个选中项,且该
<option>提供了value属性,则直接返回该值(即使值为空字符串) - 若选中项未显式提供
value属性,则返回该选项的文本内容 - 若选择框允许多选,
value仍然只返回“第一个选中的值”,可通过selectedOptions获取全部
以下面的选择框为例:
<select name="location" id="selLocation">
<option value="Sunnyvale, CA">Sunnyvale</option>
<option value="Los Angeles, CA">Los Angeles</option>
<option value="Mountain View, CA">Mountain View</option>
<option value="">China</option>
<option>Australia</option>
</select>要一次性取出所有被选中的值,可以利用 selectedOptions:
const selectbox = document.getElementById("selLocation")
const values = Array.from(selectbox.selectedOptions, (option) => option.value)
console.log(values)在 DOM 中每个 <option> 元素都有个 HTMLOptionElement 对象表示。为便于访问数据,该对象提供如下常用属性:
| 属性 | 类型 | 说明 |
|---|---|---|
index | 数字 | 当前选项在 options 集合中的索引 |
label | 字符串 | 显示在 UI 上的标签文本,等价于 label 属性 |
selected | 布尔值 | 是否被选中,设置为 true 会选中该选项 |
text | 字符串 | 选项的可见文本 |
value | 字符串 | 选项的值,等价于 value 属性 |
disabled | 布尔值 | 是否禁用该选项 |
form | HTMLFormElement | 指向所属的 <form> 元素(若存在) |
虽然可以使用常规的 DOM 功能来访问这些信息,但效率较低:
const selectbox = document.forms[0].elements["location"]
// 不推荐
const text1 = selectbox.options[0].firstChild.nodeValue // 获取第一项的文本
const value1 = selectbox.options[0].getAttribute("value") // 获取第一项的值
// 推荐
const text2 = selectbox.options[0].text // 选项文本
const value2 = selectbox.options[0].value // 选项值事件与交互
change 与 input
const selectEl = document.getElementById("selLocation")
selectEl.addEventListener("change", (event) => {
console.log("选项变更", event.target.value)
})
selectEl.addEventListener("input", (event) => {
// 某些浏览器会同步触发 input,必要时做防抖/节流
console.log("输入事件", event.target.value)
})change在选择项确定后立即触发,不需要等待blur- 需要实时联动时监听
change更稳妥;input可作为补充 - 若控件被禁用后重新启用,可根据业务需要手动触发一次更新事件
focus 与 blur
选择框同样会触发 focus、blur,可用于给出操作提示或执行校验:
const helper = document.getElementById("selectHelp")
selectEl.addEventListener("focus", () => {
helper.textContent = "使用上下键或输入首字母可以快速选中"
})
selectEl.addEventListener("blur", () => {
helper.textContent = ""
})搭配 aria-describedby 可以让读屏软件感知提示内容。
联动选择器示例
const countrySelect = document.getElementById("country")
const citySelect = document.getElementById("city")
const cityMap = {
China: ["Beijing", "Shanghai", "Shenzhen"],
USA: ["Sunnyvale", "Los Angeles", "Austin"]
}
countrySelect.addEventListener("change", () => {
const cities = cityMap[countrySelect.value] ?? []
citySelect.options.length = 0
for (const city of cities) {
citySelect.add(new Option(city, city))
}
citySelect.disabled = cities.length === 0
})根据需要可以在末尾设置 citySelect.selectedIndex = 0 并手动触发 change,确保联动后的默认值同步。
选择选项
selectedIndex
对于单选框,访问选中项的最简单方式,就是使用选择框的 selectedIndex 属性:
const selectEl = document.getElementById("selLocation")
const selectedIndex = selectEl.selectedIndex
if (selectedIndex === -1) {
console.log("Nothing selected")
} else {
const selectedOption = selectEl.options[selectedIndex]
console.log(`Selected index: ${selectedIndex}`)
console.log(`Selected text: ${selectedOption.text}`)
console.log(`Selected value: ${selectedOption.value}`)
}对于多选框,获取 selectedIndex 属性返回选中的第一项的索引。但设置 selectedIndex 会移除所有选项,只选择指定的项。
selected
通过取得选项的引用并将其 selected 属性设置为 true 来设置选中:
selectbox.options[0].selected = true⚠️ 与
selectedIndex不同,在多选框中设置选项的selected属性,不会取消对其他选中项的选择,因而可以动态选中任意多个项。但在单选选择框中,修改某个选项的selected属性则会取消对其他选项的选择。 注意:将selected属性设置为false对单选选择框没有影响 实际上selected属性的作用主要是,确定用户选择了哪一项。要取得所有选中的项,可以循环遍历选项集合并判断selected属性:
function getSelectedOptions(selectEl) {
const result = []
for (const option of selectEl.options) {
if (option.selected) {
result.push(option)
}
}
return result
}
const selectEl = document.getElementById("selLocation")
const selectedOptions = getSelectedOptions(selectEl)
let message = ""
for (const option of selectedOptions) {
message += `Selected index: ${option.index}\n`
message += `Selected text: ${option.text}\n`
message += `Selected value: ${option.value}\n`
}
console.log(message)
// 若只需值数组,可直接映射
const values = selectedOptions.map((option) => option.value)添加选项
可以使用 JavaScript 动态创建选项,并将它们添加到选择框:
- 通过 DOM API 创建节点
- 使用
Option构造函数 - 调用选择框的
add方法
const selectbox = document.getElementById("selLocation")
// 1. DOM 创建
const option1 = document.createElement("option")
option1.value = "Option value"
option1.textContent = "Option text"
selectbox.append(option1)
// 2. 构造函数
const option2 = new Option("Option text", "Option value")
selectbox.append(option2)
// 3. add 方法(第二个参数为参考节点,可传 null/undefined 追加到末尾)
const option3 = new Option("Inserted before first", "before")
selectbox.add(option3, selectbox.options[0] ?? null)append/appendChild适合批量插入,可先构建DocumentFragment后统一追加Option构造函数语法简洁,但不便携带自定义属性add支持指定插入位置,API 与旧规范兼容
如果想将新选项插入列表中间,可以配合 insertBefore 或给 add 传入参考项。
移除选项
移除选项的方式有很多种:
- 使用
removeChild:传入要移除的选项节点 - 使用选择框的
remove(index)方法 - 直接设置
selectbox.options.length = 0或selectbox.innerHTML = ""(注意:后者会重建 DOM,可能丢失事件监听)
const selectbox = document.getElementById("selLocation")
// 移除第一个选项
selectbox.removeChild(selectbox.options[0])
selectbox.remove(0)
// 清空所有选项
while (selectbox.options.length > 0) {
selectbox.remove(0)
}
// 或者更简洁:
selectbox.options.length = 0移动和重排选项
使用 appendChild 可以将一个选择框中的选项直接移动到另一个选择框:
const selectbox1 = document.getElementById("selLocations1")
const selectbox2 = document.getElementById("selLocations2")
// 将所有选中项移动到目标选择框
for (const option of Array.from(selectbox1.selectedOptions)) {
selectbox2.appendChild(option)
}重排选项次序最好的方式是使用 insertBefore 或 add 的第二个参数:
const selectbox = document.getElementById("selLocation")
// 将索引为 2 的选项移动到索引为 0 的位置
const optionToMove = selectbox.options[2]
selectbox.insertBefore(optionToMove, selectbox.options[0])
// 使用 add API 达到相同效果
selectbox.add(optionToMove, selectbox.options[0])表单集成与校验
-
使用
new FormData(form)可以一次性读取表单中所有选择框的值,多选字段会返回多条记录:javascriptconst form = document.querySelector("form") form.addEventListener("submit", (event) => { event.preventDefault() const data = new FormData(form) console.log(data.get("location")) console.log(data.getAll("hobbies")) // 针对多选 }) -
选择框同样支持原生的约束校验(
required、multiple、size等)。可以通过selectbox.setCustomValidity()自定义错误消息 -
添加
<label for="selectId">可以显著提升无障碍体验,多选时可额外提供帮助文本说明如何选择多项(例如提示使用Ctrl/Cmd)
批量更新与性能
在批量更新大量选项时,避免逐项插入造成的反复重排或闪烁:
function populateSelect(selectEl, items) {
const fragment = document.createDocumentFragment()
for (const item of items) {
const option = new Option(item.label, item.value)
fragment.append(option)
}
selectEl.replaceChildren(fragment)
}DocumentFragment可显著降低插入成本;旧浏览器可退化为appendChildreplaceChildren会一次性替换内容,必要时事先缓存旧选中值再恢复- 需要清空时比
innerHTML = ""更高效且不会执行字符串解析
加载远程数据的常见模式:
async function fetchOptions(url, selectEl, { placeholder } = {}) {
selectEl.disabled = true
selectEl.options.length = 0
if (placeholder) {
selectEl.add(new Option(placeholder, ""))
}
try {
const response = await fetch(url)
const data = await response.json()
for (const { label, value } of data) {
selectEl.add(new Option(label, value))
}
} catch (error) {
console.error("加载选项失败", error)
selectEl.setCustomValidity("暂时无法加载选项,请稍后再试")
} finally {
selectEl.disabled = false
}
}加载过程中可设置 aria-busy="true" 或展示骨架屏,避免用户误以为控件不可用。
可访问性与用户体验
- 为
<select>提供语义化的<label>,必要时通过aria-describedby关联提示文本 - 多选框应明确告知“按住 Ctrl/Cmd 多选”等操作方式
- 移动端通常渲染为原生滚轮式 UI,避免过度自定义导致可点击面积不足
- 长列表建议提供搜索、分组或首字母跳转功能,降低滚动成本
- 自定义样式时若隐藏原生选择框,需要额外实现键盘操作和焦点管理
常见陷阱
- 遗漏
value属性会导致提交到后端的是文本内容,后续改文案可能破坏数据 - 在多选框上读取
selectEl.value只返回第一个值,应遍历selectedOptions - 动态插入
<option>后立即读取selectedIndex,需确保浏览器完成渲染(可延迟到requestAnimationFrame) - 删除或禁用选项后忘记重置表单校验状态,可能保留旧的错误提示
- 在 React/Vue 等框架中同时操作 DOM 与受控组件
value,会让 UI 与状态不同步