<label>:点文字也能操作控件
<label> 给一个表单控件提供可点击的文字说明。它的价值有两层:其一,把 click 与焦点转发给关联控件——点文字等于点控件本身,checkbox / radio 的命中区因此从那个小方块放大到整段文字;其二,它在无障碍树里给控件一个可访问名称(accessible
name),屏幕阅读器读到控件时能念出这段文字。关联方式有显式(for 指向 id)与隐式(把控件包进 <label>)两种,而且只有名单内的labelable 元素才能被标记。本页把这两种关联、IDL 接口与 hidden 例外当场点出来。控件如何归属一张表单,见
<form> 元素与控件关联;各 type 的取值差异见 input 的 type states。
1 · 两种关联:显式 for 与隐式包裹
左侧用 <label for="id"> 显式指向一个 id;右侧把 <input> 直接包进 <label>,无需任何 id。点文字(而非方块),看复选框被勾选——这正是命中区被放大的效果;底部 readout 记录每次 click 转发到了哪个控件、以及它
.checked 的变化。文本框那行点 label 文字会让它聚焦。
显式与隐式的取舍。隐式包裹无需 id,结构紧凑、不怕 id 重名,适合即写即用的局部控件;但要求 label 与控件在 DOM 上是祖先 / 后代关系。显式 for 更灵活:label 与控件可以分处布局的两端(如表格中表头单元格里的 label
指向数据行的控件),只要 id 对得上即可。两者效果完全等价——都给控件一个可访问名称、都转发 click 与焦点。
2 · .labels / .control inspector:一个控件、两个 label
一个控件可以被多个 label 关联。下面给同一个 checkbox 配了两段 for 相同的 label——点任意一段都能勾选,control.labels 里会有两项。对照下方:给 <input type="hidden"> 配 label 无效,因为 hidden input 不在
labelable 名单里,它的 .labels 为空、点击毫无反应。
labelable 元素(可被 <label> 标记) |
说明 |
|---|---|
| button | 含 type=submit/reset/button。 |
| input |
除 type="hidden" 外的全部 input——text / checkbox / radio / range / file / date…
|
| meter | 度量条 <meter>。 |
| output | 计算结果 <output>。 |
| progress | 进度条 <progress>。 |
| select | 下拉 / 列表框 <select>。 |
| textarea | 多行文本 <textarea>。 |
| custom element | form-associated custom element(自定义元素声明 formAssociated 后)。 |
显式 for="id" |
隐式包裹 | |
|---|---|---|
| 写法 | <label for="x">…</label> + 控件带 id="x" |
$<label>\dots <input>\dots </label>$,控件作后代 |
| 需要 id | 需要(且要唯一) | 不需要 |
| 位置 | 可分处 DOM 任意两端,只要 id 对上 | 必须是 label 的后代 |
若同一 <label> 上同时写了 for 又包裹了控件,以 for 指向的元素为准;<label> 最多关联一个 labelable 后代。
3 · 同一份代码里的三种关联
<!-- 显式:for 指向控件 id(可分处布局两端) -->
<label for="news">订阅邮件</label>
<input type="checkbox" id="news" name="news" />
<!-- 隐式:把控件包进 label,无需 for / id -->
<label>
<input type="checkbox" name="news" />
订阅邮件
</label>
<!-- 同一控件,多个 label:labels 收齐全部,点任一都生效 -->
<label for="agree">我同意</label>
<input type="checkbox" id="agree" />
<label for="agree">(条款与隐私政策)</label>
<!-- hidden 不在 labelable 名单:关联无效 -->
<label for="csrf">无效</label>
<input type="hidden" id="csrf" />
// IDL:控件 ↔ label 双向可读
news.labels // NodeList,含全部关联它的 <label>
news.labels.length // 2(被两段 label 关联)
news.labels[0].control // 指回 news(=同一控件)
news.labels[0].htmlFor // "news"
document.querySelector('input[type=hidden]').labels.length // 0
可访问性才是 <label> 的核心价值。关联后,控件在无障碍树里获得了可访问名称——屏幕阅读器聚焦该控件时会念出 label 文字(「订阅邮件,复选框,未勾选」)。没有 label 的裸控件只能念出类型,用户无从知道它代表什么。放大命中区只是顺带的好处;给每个控件一个
label,是表单可访问的基线。
type="hidden" 与普通元素不可标记。labelable 名单是封闭的:把 for 指向 <input type="hidden">、<div>、<span> 等非 labelable
元素时,关联不成立——这些元素没有 .labels,点 label 不会转发任何行为。需要给一段非控件文字做点击交互,应改用真正的控件或 role + JS,而非指望 <label>。
隐式无需 id,显式 for 更灵活。两者并非优劣关系:隐式包裹省掉了 id 管理(在组件化、列表渲染里尤其省事);显式 for 则可以把 label 关联到 DOM 里别处的控件——配合 <form> 元素的
form 属性,label、控件、表单三者可以各在一处而仍正确联动。同一控件被多个 label 关联时,control.labels 会收齐全部,点其中任意一个都生效。