HTML无原生树形菜单组件,必须用ul/li嵌套结构+CSS动画+JS控制展开收起;details/summary仅适合简单静态场景,因兼容性差、不支持多级联动且样式难定制。

HTML 本身不提供树形菜单组件,必须靠 CSS + JavaScript 实现展开/折叠逻辑;纯 HTML <details> 标签虽能模拟,但兼容性和样式控制弱,生产环境不推荐直接用。
用 <details> 快速原型(仅限简单场景)
Chrome/Firefox/Edge(≥79)支持 <details> + <summary>,语义清晰、无需 JS,适合文档类静态菜单:
<details>
<summary>前端工具</summary>
<ul>
<li>Webpack</li>
<li>Vite</li>
</ul>
</details>但注意:<details> 默认无法多级嵌套展开(部分浏览器会重置子 <details> 的 open 状态);<summary> 样式难定制(比如加图标、hover 效果);Safari 对嵌套支持不稳定;服务端渲染时所有节点默认闭合,无法初始展开指定层级。
用原生 JS 实现可复用的树形菜单(推荐)
核心是监听点击、切换 class、控制 display 或 max-height 动画。关键点:
立即学习“前端免费学习笔记(深入)”;
- 结构用
<ul>/<li>嵌套,每个父节点带data-toggle="tree-node"和aria-expanded - 折叠状态存在
aria-expanded属性里,兼顾可访问性 - 用
classList.toggle()切换is-open类,CSS 控制max-height过渡(比display更易做动画) - 避免用
querySelectorAll('li > ul')全局查子菜单——应只操作当前节点下的第一个<ul>,防止误触兄弟节点
示例 JS 片段:
document.addEventListener('click', e => {
const btn = e.target.closest('[data-toggle="tree-node"]');
if (!btn) return;
const menu = btn.nextElementSibling;
if (!menu || menu.tagName !== 'UL') return;
const isOpen = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', !isOpen);
menu.classList.toggle('is-open');
});用 CSS 控制展开动画和图标状态
动画依赖 max-height 而非 height(后者无法过渡 auto),需预估最大高度或用 JS 动态设值。图标旋转用 transform: rotate(90deg) 最稳:
.tree-node__toggle::after {
content: '▶';
display: inline-block;
margin-left: 4px;
transition: transform 0.2s;
}
.tree-node__toggle[aria-expanded="true"]::after {
transform: rotate(90deg);
}
.tree-node__children {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.tree-node__children.is-open {
max-height: 500px; /* 需足够容纳最深子菜单 */
}注意:max-height 值不能写死为 none(无法过渡),也不能太小导致截断;图标用伪元素比 <i> 更轻量,且可随 aria-expanded 精确同步状态。
第三方库选型避坑(如需快速集成)
若项目已用 Vue/React,优先用对应生态组件(如 Vue Tree View、react-sortable-tree),而非全局引入 jQuery 插件。如果必须用原生库,注意:
-
js-tree功能强但体积大(~100KB),仅需基础展开/折叠时杀鸡用牛刀 -
awesomplete是自动补全库,不是树形菜单——搜错关键词容易误引入 - 很多 npm 上标“tree view”的包实际是文件系统浏览器,带拖拽/右键菜单等冗余功能,增加 bundle 体积
- 检查 package.json 的
peerDependencies:若要求jQuery@^3.0但项目已迁移到原生 JS,就直接排除
真正轻量可用的只有手写 50 行以内的代码——结构清晰、无外部依赖、CSS 可完全覆盖,才是多数业务场景下最可控的选择。


















