如何在 HTML 中制作下拉列表
下拉列表就是一个 <select> 元素,每个选项对应一个 <option>。每个选项的 value 是表单要发送的内容,标签之间的文字是用户看到的内容。选择一项,然后点击 Submit:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Select</title>
</head>
<body>
<form>
<label for="lang">Course language</label>
<select id="lang" name="language">
<option value="py">Python</option>
<option value="js" selected>JavaScript</option>
<option value="java">Java</option>
<option value="cpp">C++</option>
</select>
<button type="submit">Submit</button>
</form>
</body>
</html>
表单发送的是 language=js:select 的 name 和所选选项的 value。selected 设置默认选项;不加的话,默认选中第一个选项。如果某个选项没有 value,就会改为发送它的文字。
用 for 和 id 把标签和 select 关联起来(或者用标签把它包住),这样点击标签就能让下拉列表获得焦点,屏幕阅读器也会读出它。
select 和 option 的属性
| 元素 | 属性 | 作用 |
|---|
<select> | name | 发送值时使用的键名 |
<select> | required | 必须选择一个真正的选项,而不是空的第一个占位选项 |
<select> | multiple | 可以选择多个选项 |
<select> | size | 可见的行数(会把它变成列表框) |
<select> | disabled | 整个下拉列表被禁用,也不会被发送 |
<option> | value | 选中它时发送的内容 |
<option> | selected | 页面加载时被选中 |
<option> | disabled | 显示出来,但不能选择 |
<optgroup> | label | 这一组的标题 |
带 required 的占位选项
select 总是有一个值,所以如果没有占位选项,即使用户从没碰过下拉列表,第一个真正的选项也会被发送。一个空的第一选项可以解决这个问题,而 required 会在选中真正的选项之前阻止提交:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Placeholder</title>
</head>
<body>
<form>
<label>Country
<select name="country" required>
<option value="" disabled selected>Choose a country</option>
<option value="br">Brazil</option>
<option value="de">Germany</option>
<option value="jp">Japan</option>
</select>
</label>
<button type="submit">Continue</button>
</form>
</body>
</html>
不做选择就点击 Continue:浏览器会拒绝提交,并提示你选择一项。占位选项设置了 value="",required 会把它当作空值;它还设置了 disabled,所以在选好国家之后,就不能再选回它。
用 optgroup 给选项分组
<optgroup label="..."> 给一组选项加上一个标题。这个标题不能被选择:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>optgroup</title>
</head>
<body>
<form>
<label>Car
<select name="car">
<optgroup label="Electric">
<option value="model3">Model 3</option>
<option value="ioniq">Ioniq 5</option>
</optgroup>
<optgroup label="Hybrid">
<option value="prius">Prius</option>
<option value="corolla">Corolla Hybrid</option>
</optgroup>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
分组不能嵌套:一个列表只有一层标题,不能更多。给 <optgroup> 加上 disabled,会禁用组内的所有选项。
多选
multiple 让用户可以选择多个选项,并把下拉列表变成一个列表框。按住 Ctrl(Mac 上是 Cmd)可以逐个添加选项,按住 Shift 则可以选择一个范围:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Multiple select</title>
</head>
<body>
<form>
<label for="skills">Skills</label><br>
<select id="skills" name="skills" multiple size="5">
<option value="html" selected>HTML</option>
<option value="css" selected>CSS</option>
<option value="js">JavaScript</option>
<option value="sql">SQL</option>
<option value="git">Git</option>
</select>
<p><button type="submit">Submit</button></p>
</form>
</body>
</html>
表单发送的是 skills=html 和 skills=css:每个选中的选项各发送一次,名称都相同。很多人不知道 Ctrl 加点击这个操作,所以选项不多时,用一组复选框更好用。
在 JavaScript 中读取和设置值
select.value 可以读取和设置所选选项的值,用户选择了另一个选项时会触发 change 事件。一个常见用法是联动下拉列表:第二个下拉列表的选项取决于第一个:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dependent dropdowns</title>
<style>
body { font-family: system-ui, sans-serif; }
label { display: block; margin-bottom: 8px; }
</style>
</head>
<body>
<form>
<label>Country
<select name="country" id="country">
<option value="fr">France</option>
<option value="it">Italy</option>
</select>
</label>
<label>City <select name="city" id="city"></select></label>
<button type="submit">Submit</button>
</form>
<p id="out"></p>
<script>
const cities = { fr: ['Paris', 'Lyon', 'Nice'], it: ['Rome', 'Milan'] };
const country = document.querySelector('#country');
const city = document.querySelector('#city');
const fill = () => {
city.replaceChildren(...cities[country.value].map((name) => new Option(name, name.toLowerCase())));
};
const show = () => {
document.querySelector('#out').textContent =
'value: ' + city.value + ', text: ' + city.selectedOptions[0].text;
};
country.addEventListener('change', () => { fill(); show(); });
city.addEventListener('change', show);
fill();
show();
</script>
</body>
</html>
new Option(text, value) 创建一个选项。select.selectedIndex 是所选选项的位置,select.selectedOptions 列出所有选中的选项,读取多选 select 的值就要用它。
select 的样式
下拉列表展开后的列表由浏览器和操作系统绘制,能设置样式的地方很少。收起时的框则可以:appearance: none 去掉原生箭头,再用背景图片放上你自己的箭头:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Styled select</title>
<style>
body { font-family: system-ui, sans-serif; }
.styled {
appearance: none;
font: inherit;
padding: 8px 36px 8px 12px;
border: 2px solid #9ca3af;
border-radius: 8px;
background-color: #fff;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23111827' stroke-width='2'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
color: #111827;
}
.styled:focus-visible { outline: 3px solid #2563eb; outline-offset: 2px; }
</style>
</head>
<body>
<label>Default <select><option>Option one</option><option>Option two</option></select></label>
<br><br>
<label>Styled
<select class="styled">
<option>Option one</option>
<option>Option two</option>
</select>
</label>
</body>
</html>
末尾额外的内边距给箭头留出空间,这样较长的选项文字不会跑到箭头下面。font: inherit 在这里和在按钮上一样重要:select 默认使用浏览器较小的表单字体。展开列表中的选项仍然保持原生外观。在 Chrome 中,用 appearance: base-select 主动开启后,展开的列表也能设置样式;不支持的浏览器会显示原生列表。要在所有浏览器中完全掌控外观,就得用 ARIA 自己做一个组件,要做好它,工作量比看起来大得多。
select、单选按钮还是 datalist?
| 用什么 | 什么时候用 |
|---|
<select> | 从一长串选项中选一个,或者空间有限时 |
| 单选按钮 | 从少量选项中选一个,并且所有选项都应该直接可见(参见 HTML 单选按钮) |
带 <datalist> 的 <input> | 给出建议,但允许输入任何值 |
常见错误
- 必选项没有占位选项。即使用户从没选过,第一个真正的选项也会被发送。
- 占位选项带有真实的值,比如
value="none"。required 会把它当作有效的选择。请使用 value=""。
- 文字较长或需要翻译时,选项没有
value。服务器收到的是显示的文字,而它会随语言变化。
- 选项不多却用
multiple。复选框更好用。
- 在选项上监听
click。请在 <select> 上监听 change,它在用键盘改变选择时也会触发。
- 没有标签。只有占位选项的 select,一旦选中某个选项,就没有名称了。