待办事项清单应优先用 <ul> 而非 <ol>,因其本质是状态集合而非有序序列;需用 role="list"、aria-checked 等保障可访问性,避免语义混乱与读屏干扰。

用 <ol> 做待办事项清单,但别真用它
HTML 的 <ol> 语义是“有序序列”,比如步骤说明、排名列表。待办事项本质是「未完成/已完成」的状态集合,不是天然有顺序依赖的序列——哪怕你写了 1. 买菜、2. 煮饭、3. 洗碗,用户也可能先洗碗再煮饭。浏览器和屏幕阅读器会把 <ol> 当作严格序号流处理,反而干扰可访问性逻辑。
实际项目中,90% 的待办清单用 <ul> 更合理,靠 CSS 控制视觉序号(如 list-style: decimal 或伪元素),同时用 role="list" 和 aria-checked 等属性表达状态。
<ol> 里怎么加复选框?别直接塞 <input type="checkbox">
在 <ol> 里混写 <li><input type="checkbox"> 打扫房间</li> 看似能用,但会破坏语义层级:复选框是交互控件,<li> 是内容容器,两者职责不该耦合。更糟的是,部分读屏软件可能跳过 <input> 的标签关联,导致“打扫房间”无法被正确朗读为可操作项。
- 正确做法:每个待办项用
<li role="listitem">包裹一个带aria-label的<input type="checkbox">,并用<label>显式绑定 - 示例:
<li role="listitem"> <input type="checkbox" id="task-1" aria-label="完成打扫房间"> <label for="task-1">打扫房间</label> </li>
- 如果坚持用
<ol>,必须加role="list"属性,否则某些旧版读屏器可能忽略其列表语义
想让序号随勾选状态动态变化?别碰 start 属性
有人想实现“已勾选的项自动从列表中移除,剩余项序号重排”,于是尝试用 <ol start="2"> 手动调整起始值。这完全不可行:start 是静态属性,无法响应 DOM 变化;且重排序号会误导辅助技术——屏幕阅读器仍会按原始 DOM 顺序播报,而视觉序号已变,造成不一致。
立即学习“前端免费学习笔记(深入)”;
- 真正可行的方案:隐藏已勾选项(
display: none或aria-hidden="true"),让剩余项保持原始 DOM 顺序,仅视觉上“看起来重排” - 如果需要真实重排(比如拖拽排序),必须用 JavaScript 重新生成整个
<ol>或<ul>,不能靠属性微调 - 注意:用
display: none隐藏时,确保同时设置aria-hidden="true",避免读屏器仍尝试读取不可见内容
CSS 控制序号样式的坑:counter-reset 和 counter-increment 不是万能解
用 CSS 计数器模拟序号(counter-reset + ::before)确实灵活,但容易忽略两个硬限制:
- 计数器无法跨元素继承——如果待办项分散在多个
<ol>或嵌套容器里,序号会各自从 1 开始,除非手动管理counter-reset作用域 - 计数器不触发可访问性暴露:视障用户看不到伪元素生成的数字,也不会被读屏器识别为序号,纯属视觉装饰
- 更稳妥的替代:用
<ol>配合list-style-type: decimal(默认行为),或改用<ul>+aria-setsize/aria-posinset手动声明位置信息
复杂交互场景下,序号只是表象,状态同步和语义准确才是关键。别为了视觉上的“1. 2. 3.”牺牲可访问性和维护成本。



















