多级列表嵌套合法但默认不显示“1.1”类编号,需用css counter实现;嵌套建议≤3层,过深应改用details或js树形控件;li必须显式闭合,ul/ol内不可直接放div;语义决定嵌套类型,非视觉需求。

多级列表不能靠 <ul></ul> 和 <ol></ol> 嵌套自动产生“1.1”“2.3.1”这类编号,浏览器默认只重置子列表计数器,不继承父级序号。真要显示层级编号,必须用 CSS counter 手动控制。
嵌套本身合法但默认不显示层级编号
HTML 允许在 <li> 里直接放 <ul></ul> 或 <ol></ol>,结构完全合规,比如:
<ol>
<li>第一步</li>
<li>第二步</li>
<li>第三步
<ol>
<li>子步骤一</li>
<li>子步骤二</li>
</ol>
</li>
</ol>
但浏览器渲染时,内层 <ol></ol> 会从 1 开始编号,不是 3.1、3.2 —— 这是规范行为,不是 bug。
- 嵌套层级建议 ≤3 层,再深就该考虑用
<details></details>或 JS 树形控件 -
<li>必须显式闭合;写成<li>a</li> <li>b</li>会导致解析异常(第一个<li>被隐式闭合,DOM 结构错乱) - 不要把
<div> 直接塞进 <code><ul></ul>或<ol></ol>,只允许<li>作为直接子元素CSS counter 实现 1.1、1.2.1 这类编号
要用
counter-reset和counter-increment搭配counters()函数生成嵌套编号:.multi-level { counter-reset: section; } .multi-level > li { counter-increment: section; } .multi-level > li::before { content: counters(section, ".") ". "; } .multi-level ol { counter-reset: section; margin-left: 20px; }关键点:
-
counters(section, ".")中的"."是分隔符,可换成"-"或"" - 子
<ol></ol>必须重新counter-reset,否则会累加而非分层计数 -
::before插入内容不影响可访问性,但屏幕阅读器不会读出这个合成编号,所以文本里最好自带“第二步:”这类提示
Ul 和 Ol 混用要看语义,不是看缩进效果
选择嵌套类型取决于内容逻辑,而不是“想让它缩进”或“想换个符号”:
- 主项并列、子项有序 → 用
- ...
- ...
- ...
- 主项有序、子项也有序 → 用
<ol><li>...<ol>...</ol> </li></ol> - 纯为视觉缩进换符号?改用
list-style-type 或 <code>::before更合适,别滥用嵌套
例如菜谱:“准备材料”是并列项(
<ul></ul>),“开始烹饪”下是顺序动作(<ol></ol>),混用反而更准确。用 details/summary 快速实现可折叠目录树
如果目标是交互式目录(点击展开/收起),优先用原生
<details></details>+<summary></summary>:<details><summary>前端开发</summary><ul> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul></details>优势明显:
- 无需 JS 就支持展开/折叠,语义清晰
- 天然兼容屏幕阅读器,能正确播报“已折叠”“已展开”状态
- Chrome 12+、Firefox 49+、Edge 79+ 全支持,移动端也稳
真正难的不是怎么嵌套标签,而是判断哪些层级该由 HTML 表达、哪些该交给 CSS 控制、哪些必须用 JS 交互 —— 混淆这三者的边界,才是后期维护最头疼的地方。
-










