HTML规范强制规定ol的直接子元素只能是li(及script、template等极少数例外),非法内容会被浏览器不可控修复,导致结构断裂、编号重置、语义丢失;嵌套ol必须完整置于li内,否则type、start等属性失效,层级编号无法继承。

浏览器解析 ol 时只认 li 作为合法子元素
HTML 规范强制规定 <ol> 的直接子元素只能是 <li>(以及 <script>、<template> 等极少数例外)。一旦你在 <ol> 里直接写文本、<p>、<div> 或另一个 <ol>,浏览器就会尝试“修复”结构——但这个修复过程不可控,常见结果是:把非法内容移出列表、自动闭合外层 <ol>、再新开一个 <ol>。这不是 bug,是规范行为。
嵌套 ol 不在 li 里会导致编号重置为 1
当你写成 <ol><li>第一步</li><ol><li>子步骤</li></ol></ol>,内层 <ol> 实际上被浏览器当作一个全新、独立的列表处理。它和外层没有父子关系,编号自然从 1 开始,而不是 “1.1” 或 “2.1”。你看到的 “A. B.” 变成 “A. A.”,根源就在这里。
- 验证方法:用 DevTools 选中内层
<ol>,看它的父节点是不是某个<li> - 如果父节点是
<body>或外层<ol>,说明结构非法 - 屏幕阅读器也会把两个列表读作“第一个有序列表”“第二个有序列表”,丢失层级语义
type 和 start 属性对跨 li 的嵌套完全无效
type、start、reversed 这些属性只作用于当前 <ol> 元素本身,且必须写在 <ol> 开始标签上。它们不会穿透到未被包裹在 <li> 中的子列表里:
-
<ol type="A"><li>A. 主项</li><ol type="1"><li>1. 子项</li></ol></ol>→ 内层type="1"被忽略(因为内层<ol>不在<li>内) -
<ol start="5"><li>5. 第一项</li><ol><li>1. 错误子项</li></ol></ol>→ 子项仍显示为 1,不受外层start影响 - 想实现 “1.1”、“A.2.b” 这类编号,必须靠 CSS
counter(),前提是 DOM 结构先合法
li 是唯一能承载语义层级的容器节点
<li> 不只是视觉占位符,它是 HTML 列表模型中唯一被定义为“可包含嵌套列表”的语义单元。所有嵌套意图——无论是步骤中的子步骤、问答中的展开项,还是大纲中的子条目——都必须通过 <li> 来锚定。跳过它,等于放弃语义;用 <div> 或 <p> 替代,只会让结构断裂、样式失效、辅助技术误读。
立即学习“前端免费学习笔记(深入)”;
最容易被忽略的一点:HTML5 允许省略 </li>,但嵌套时显式闭合更安全。尤其当内部有 <ol> 或 <ul> 时,不闭合 <li> 容易导致浏览器误判结束位置,间接造成嵌套错位。



















