ol嵌套必须严格遵循父子关系,子ol必须作为li的直接子元素,否则浏览器视为新列表导致编号重置;type属性仅控制单层样式,多级编号如1.1需CSS counters实现。

ol嵌套必须严格遵循父子关系才能生效
浏览器对<ol>的层级编号逻辑,完全依赖DOM嵌套结构。如果子<ol>写在父<ol>外部(哪怕只差一个</li>闭合),它就被视为全新列表,编号必然重置为A或1。
常见错误现象:第二级显示A.、B.、C.,但本该是B.、C.、D.;或者数字突然跳回1;甚至屏幕阅读器读出错乱序号。
- 所有子
<ol>必须作为<li>的直接子元素存在,不能放在<p>、<div>或<ol>外层 - 每个
<li>里若含子列表,必须先有文本内容(或<strong>等行内元素),再跟<ol>,不能空<li></li>后补 - 用浏览器开发者工具检查DOM树,确认子
<ol>是否真的挂在父<li>下,而非悬浮在同级
type属性控制单层样式,不跨层级继承
type是HTML原生属性,只作用于当前<ol>,值包括"1"、"a"、"A"、"i"、"I"。它不自动“延续上一级编号”,也不会把父级的A.变成A.1.——那是CSS计数器的事。
容易踩的坑:
立即学习“前端免费学习笔记(深入)”;
- 写
<ol type="A"><ol type="1">时,以为第二层会带A.前缀,实际只是独立的1.、2.、3. -
type值不区分大小写,但建议统一用小写(如type="a"),部分旧IE对type="A"解析异常 - 和CSS的
list-style-type混用时,后者优先级更高;一旦用了ol { list-style-type: lower-greek; },type就失效
CSS counters实现真正的多级编号(如1.1、1.1.1)
要生成带点分隔的嵌套编号(比如条款编号、文档章节),必须用CSS计数器。核心是为每层定义独立计数器,并用counters()函数拼接。
示例:让第一层为1.、第二层为1.1、第三层为1.1.1
ol {
counter-reset: section;
}
ol ol {
counter-reset: subsection;
}
ol ol ol {
counter-reset: subsubsection;
}
ol li::before {
content: counters(section, ".") ". ";
counter-increment: section;
}
ol ol li::before {
content: counters(section, ".") "." counters(subsection, ".") ". ";
counter-increment: subsection;
}
ol ol ol li::before {
content: counters(section, ".") "." counters(subsection, ".") "." counters(subsubsection, ".") ". ";
counter-increment: subsubsection;
}
关键点:
-
counter-reset必须设在每层<ol>上,不是<li> -
counters(counter-name, ".")会自动拼接父级值,比手动写counter(section)+"."+counter(subsection)更可靠 - 若某层需从特定值开始(如第二层从10开始),写
counter-reset: subsection 9;(因为它是“起始前一个数”)
value属性和start属性混用会破坏连续性
start设整个列表起点,value强行覆盖单个<li>显示值。两者同时出现时,start优先生效,value只影响那个<li>本身,后续仍按value + 1递增——极易造成跳跃、重复、倒退。
典型反例:
<ol start="5"> <li>第五项</li> <li value="12">第十二项</li> <li>第十三项</li> </ol>
渲染结果是:5. 第五项 → 12. 第十二项 → 13. 第十三项。这不是“从12续编”,而是打断了线性逻辑。
可访问性风险更大:屏幕阅读器可能按DOM顺序读1、2、3,却看到5、12、13,语义断层无法修复。
真正需要跳号或非线性编号时,应放弃start/value,改用CSS计数器配合JS动态赋值(如counter-set或counter-increment: section 7;),并同步更新aria-posinset。



















