ul嵌套必须将子ul置于li内,否则破坏语义、可访问性及SEO;li是语义单元而非容器,应保持单一逻辑项;横向菜单宜用flex但保留li语义;纯目录优先用details原生组件。

ul 嵌套列表写不对,不是样式出问题,而是 DOM 结构断了——屏幕阅读器读不出层级、键盘 Tab 跳不到子项、JS parentElement 找不到父 li,连 SEO 都可能降权。
ul 里只能直接放 li,子 ul 必须塞进某个 li 里面
这是 HTML5 的硬性语法要求,不是“建议”。浏览器容错渲染会帮你补 li 或忽略非法子元素,但语义和可访问性已经失效。
-
ul的直接子元素只能是li;写<ul><div></div></ul>或<ul><h3>标题</h3></ul>会触发 W3C 校验错误 - 子
ul必须作为某个li的**直接子内容**,比如:<li><ul><li>子项</li></ul></li> - 常见误写:
<ul><li>一级</li></ul><ul><li>被当成新列表了</li></ul>—— 这是两个平级列表,不是嵌套 - 嵌套层级没有上限,但超过 3 层时,屏幕阅读器播报易疲劳,CSS
margin-left缩进也容易失控
li 是语义边界,不是容器盒子
很多人把 li 当成 div 用,塞一整段文字+按钮+图片,结果键盘用户按一次 Tab 就跳过全部内容,语音朗读也变成“……长段落加按钮加图标”,信息密度爆炸。
-
li的本质是定义一个“可独立聚焦、编号、朗读”的条目单元;每个li应该对应一个逻辑上完整的操作项或内容单元 - 如果一个
li里有多个链接、按钮或交互控件,优先考虑拆成多个li,或改用dl(定义列表)/nav+button -
li默认是display: list-item,一旦设成flex或grid,项目符号(●)立刻消失——这不是 bug,是 display 类型变了;若必须改,得手动补list-style-position: inside和对齐
自定义图标或清空符号时,padding-left 容易漏调
用 background-image 替换圆点很常见,但默认 ul 有 padding-left: 40px,不重置会导致图标压字或缩进过大。
立即学习“前端免费学习笔记(深入)”;
- 第一步永远是:
ul { list-style: none; padding-left: 0; } - 第二步给
li加图标:li { background: url(arrow.svg) no-repeat left center; padding-left: 24px; }(24px 要大于图标宽度) - 别只写
list-style-type: none,它不清理list-style-image和缩进;list-style: none才真正清空所有列表样式 - 横向菜单用
ul { display: flex; }更可控,但注意li仍需保持display: list-item才能保留语义——否则 screen reader 可能跳过
多级目录想折叠?优先用 <details>,别硬套 ul/li
纯文档目录(如 API 文档、README)、不需要 JS 控制、也不用兼容 IE,<details> 是更轻量、原生可访问的方案。
-
<details>自带键盘支持(Enter/Space 展开)、焦点管理、无障碍属性(aria-expanded自动同步) - 嵌套必须写在
<summary>内部:<details><summary>A</summary><details><summary>A1</summary><p>内容</p></details></details> - 别写两个并列
<details>然后靠 CSS 拉位置——DOM 不嵌套,aria-controls和键盘导航就全废了 - 要支持 hover 展开?得加 JS;
<details>原生只响应点击和键盘
真正卡住人的从来不是怎么写嵌套,而是判断“这里到底该不该用 ul/li”——时间轴、纯图标导航、卡片网格,表面像列表,语义上却常该用 section + time、nav + button 或 article。标签写对只是起点,语义对了,后续所有交互、样式、无障碍才不会反复返工。



















