案例核心效果基础功能点击标题展开 / 折叠内容经典手风琴交互双模式切换互斥展开 / 自由展开适配不同业务场景高度自适应动画非固定 max-height精准匹配内容高度无多余空白 / 截断移动端触摸支持 键盘回车 / 空格触发全端无障碍交互嵌套二级菜单覆盖后台管理系统真实场景本地存储记忆页面刷新保留展开状态体验拉满样式升级分层阴影、hover 动效、过渡曲线优化视觉质感拉满。 实现思路HTML语义化结构使用 details/summary 语义标签兜底兼容无障碍 嵌套层级设计CSS放弃固定 max-height通过 JS 计算内容真实高度实现精准动画自定义 transition-timing-function 实现自然回弹效果响应式适配 触摸交互样式优化JS封装核心函数支持模式切换、状态记忆、嵌套处理监听点击 / 触摸 / 键盘事件全端交互闭环localStorage 持久化展开状态提升用户体验。 完整源码可直接复制运行企业级质量html预览!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0/ titleJS 手风琴折叠菜单 | 实战案例企业级版/title style /* 全局重置基础样式规范且兼容 */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: Microsoft YaHei, PingFang SC, sans-serif; } body { padding: 40px 20px; background: #f5f7fa; color: #333; } .container { max-width: 700px; margin: 0 auto; } .title { text-align: center; margin-bottom: 30px; font-size: 24px; font-weight: 600; color: #2c3e50; } /* 模式切换按钮 */ .mode-switch { text-align: center; margin-bottom: 20px; } .switch-btn { padding: 8px 16px; margin: 0 8px; border: 1px solid #409eff; border-radius: 6px; background: #fff; color: #409eff; cursor: pointer; transition: all 0.2s ease; } .switch-btn.active { background: #409eff; color: #fff; } /* 手风琴容器 */ .accordion { border-radius: 8px; overflow: hidden; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); } /* 手风琴项核心样式升级 */ .accordion-item { background: #fff; border-bottom: 1px solid #e5e6eb; } .accordion-item:last-child { border-bottom: none; } /* 标题栏交互增强 */ .accordion-header { padding: 16px 20px; font-size: 16px; font-weight: 500; cursor: pointer; position: relative; display: flex; align-items: center; transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); user-select: none; } .accordion-header:hover { background-color: #f8f9fa; } .accordion-header:focus { outline: 2px solid rgba(64, 158, 255, 0.3); outline-offset: -2px; } /* 箭头图标动效升级 */ .accordion-icon { width: 16px; height: 16px; margin-right: 12px; border-radius: 2px; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; align-items: center; justify-content: center; } .accordion-icon::after { content: ; width: 6px; height: 6px; border-right: 2px solid #666; border-bottom: 2px solid #666; transform: rotate(45deg); transition: transform 0.3s ease; } .accordion-item.active .accordion-icon::after { transform: rotate(-135deg); } /* 内容区域精准动画核心 */ .accordion-content { overflow: hidden; height: 0; transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1); padding: 0 20px; color: #555; line-height: 1.8; } .accordion-content-inner { padding: 16px 0; } /* 嵌套二级菜单样式 */ .accordion-submenu { margin: 8px 0 8px 24px; border-left: 2px solid #e5e6eb; padding-left: 16px; } .accordion-subitem { padding: 8px 0; font-size: 14px; color: #666; cursor: pointer; transition: color 0.2s ease; } .accordion-subitem:hover { color: #409eff; } /* 移动端适配精细化 */ media (max-width: 768px) { .title { font-size: 20px; } .accordion-header { padding: 14px 16px; font-size: 15px; } .accordion-content { padding: 0 16px; font-size: 14px; } .switch-btn { padding: 6px 12px; font-size: 14px; } } /style /head body div classcontainer h2 classtitle手风琴折叠菜单企业级版/h2 !-- 模式切换按钮 -- div classmode-switch button classswitch-btn active data-modeexclusive互斥展开/button button classswitch-btn data-modefree自由展开/button /div div classaccordion idaccordion !-- 一级菜单1默认展开 -- div classaccordion-item data-keymenu1 div classaccordion-header tabindex0 span classaccordion-icon/span 前端学习路线 /div div classaccordion-content div classaccordion-content-inner 从零基础到高级工程师的完整路线 div classaccordion-submenu div classaccordion-subitem基础阶段HTML → CSS → JavaScript/div div classaccordion-subitem进阶阶段ES6 → 浏览器原理 → 网络基础/div div classaccordion-subitem框架阶段Vue3 → React → 小程序/div div classaccordion-subitem工程化Webpack → Vite → Git → 自动化部署/div /div 核心原则多敲代码多做项目拒绝只看不动手。 /div /div /div !-- 一级菜单2 -- div classaccordion-item data-keymenu2 div classaccordion-header tabindex0 span classaccordion-icon/span JS实战100题计划 /div div classaccordion-content div classaccordion-content-inner 每天1个实战小案例3个月夯实前端核心能力 div classaccordion-submenu div classaccordion-subitem第1-20题DOM基础交互轮播、评分、倒计时/div div classaccordion-subitem第21-50题组件封装手风琴、日历、表单/div div classaccordion-subitem第51-80题异步编程AJAX、Promise、async/await/div div classaccordion-subitem第81-100题工程化性能优化/div /div /div /div /div !-- 一级菜单3 -- div classaccordion-item data-keymenu3 div classaccordion-header tabindex0 span classaccordion-icon/span 面试高频知识点 /div div classaccordion-content div classaccordion-content-inner 前端面试必背核心考点 div classaccordion-submenu div classaccordion-subitemJS核心闭包、原型链、事件循环、防抖节流/div div classaccordion-subitemCSS核心Flex/Grid、BFC、响应式、动画/div div classaccordion-subitem框架核心Vue响应式、React虚拟DOM、生命周期/div div classaccordion-subitem工程化打包优化、模块化、跨域解决方案/div /div /div /div /div !-- 一级菜单4 -- div classaccordion-item data-keymenu4 div classaccordion-header tabindex0 span classaccordion-icon/span 项目实战建议 /div div classaccordion-content div classaccordion-content-inner 从易到难的项目练习路径 div classaccordion-submenu div classaccordion-subitem入门级TodoList、轮播图、计算器/div div classaccordion-subitem进阶级后台管理系统、电商首页、天气应用/div div classaccordion-subitem高级级在线文档、实时聊天、可视化大屏/div /div 每个项目都要做好需求分析 → 技术选型 → 代码实现 → 优化迭代。 /div /div /div /div /div script // 核心配置可配置化体现扩展性 const ACCORDION_CONFIG { defaultActiveKey: menu1, // 默认展开项 storageKey: accordion_active_state, // 本地存储key defaultMode: exclusive // 默认模式exclusive-互斥free-自由 }; // 全局状态 let currentMode ACCORDION_CONFIG.defaultMode; let activeStates JSON.parse(localStorage.getItem(ACCORDION_CONFIG.storageKey)) || {}; // 初始化从本地存储恢复状态 function initAccordion() { const accordion document.getElementById(accordion); const items accordion.querySelectorAll(.accordion-item); // 初始化默认状态 if (Object.keys(activeStates).length 0) { activeStates[ACCORDION_CONFIG.defaultActiveKey] true; } // 渲染初始状态 items.forEach(item { const key item.dataset.key; if (activeStates[key]) { setItemActive(item, true); } }); // 绑定所有标题事件 bindHeaderEvents(); // 绑定模式切换事件 bindModeSwitchEvents(); } // 绑定标题点击/键盘/触摸事件全端交互 function bindHeaderEvents() { const headers document.querySelectorAll(.accordion-header); headers.forEach(header { // 点击事件核心 header.addEventListener(click, () { toggleItem(header.parentElement); }); // 键盘事件无障碍 header.addEventListener(keydown, (e) { if (e.key Enter || e.key ) { e.preventDefault(); toggleItem(header.parentElement); } }); // 触摸事件移动端 header.addEventListener(touchend, (e) { e.preventDefault(); toggleItem(header.parentElement); }); }); } // 绑定模式切换事件 function bindModeSwitchEvents() { const switchBtns document.querySelectorAll(.switch-btn); switchBtns.forEach(btn { btn.addEventListener(click, () { // 更新按钮状态 switchBtns.forEach(b b.classList.remove(active)); btn.classList.add(active); // 更新当前模式 currentMode btn.dataset.mode; }); }); } // 切换菜单项展开/折叠状态核心逻辑升级 function toggleItem(item) { const key item.dataset.key; const isActive item.classList.contains(active); // 互斥模式先关闭所有 if (currentMode exclusive !isActive) { document.querySelectorAll(.accordion-item).forEach(i { setItemActive(i, false); delete activeStates[i.dataset.key]; }); } // 切换当前项状态 setItemActive(item, !isActive); activeStates[key] !isActive; // 持久化到本地存储 localStorage.setItem(ACCORDION_CONFIG.storageKey, JSON.stringify(activeStates)); } // 设置菜单项激活状态精准高度计算核心 function setItemActive(item, isActive) { const content item.querySelector(.accordion-content); const contentInner item.querySelector(.accordion-content-inner); if (isActive) { // 计算内容真实高度实现精准动画 const height contentInner.offsetHeight; content.style.height ${height}px; item.classList.add(active); } else { content.style.height 0px; item.classList.remove(active); } } // 初始化执行 initAccordion(); /script /body /html 核心代码拆解1. HTML 结构语义化 可扩展新增data-key标识每个菜单项用于本地存储状态关联嵌套accordion-submenu实现二级菜单贴合真实业务场景标题添加tabindex0支持键盘聚焦符合无障碍设计规范模式切换按钮独立分区交互逻辑与菜单解耦。2. CSS 核心升级精准动画放弃固定max-height改用height:0JS 计算真实高度彻底解决内容截断 / 空白问题动效质感使用cubic-bezier(0.4, 0, 0.2, 1)自定义过渡曲线动画更自然回弹无障碍优化focus状态添加外轮廓支持键盘操作反馈分层样式通过边框、阴影、间距营造视觉层级符合企业级 UI 设计规范。3. JS 核心逻辑企业级思维✅状态持久化用localStorage存储展开状态页面刷新不丢失设计activeStates对象管理状态结构清晰易维护✅模式切换支持 “互斥展开” 和 “自由展开” 两种模式适配不同场景模式切换按钮独立绑定事件与核心逻辑解耦✅精准高度计算通过offsetHeight获取内容真实高度动画无冗余 / 截断封装setItemActive函数统一管理状态切换✅全端交互支持点击、触摸、键盘Enter / 空格三种触发方式移动端适配精细化不同屏幕尺寸样式自适应✅可配置化提取ACCORDION_CONFIG配置对象修改默认项 / 存储 key 无需改核心逻辑嵌套菜单结构可无限扩展满足复杂业务需求。 新手进阶踩坑点动画卡顿 / 不自然原因使用max-height固定值或默认ease过渡曲线解决方案改用真实高度计算 自定义贝塞尔曲线cubic-bezier(0.4, 0, 0.2, 1)本地存储状态错乱原因未给菜单项加唯一data-key状态关联失败解决方案每个accordion-item添加data-key与activeStates一一绑定键盘操作无响应原因标题无tabindex或未监听keydown事件解决方案添加tabindex0并监听 Enter / 空格键嵌套菜单样式混乱原因未做层级样式隔离解决方案通过margin/padding/border营造嵌套视觉层级避免样式污染。 高级扩展思路懒加载内容展开时异步加载菜单内容优化首屏加载速度拖拽排序结合SortableJS实现菜单拖拽排序存储排序结果权限控制根据用户权限动态显示 / 隐藏菜单项动画扩展添加内容淡入淡出 标题颜色渐变视觉效果更丰富API 联动对接后端接口动态渲染菜单列表支持增删改查暗黑模式添加暗黑模式切换适配不同主题风格。 核心知识点总结状态管理本地存储 内存状态双管理实现状态持久化与实时更新精准动画通过offsetHeight计算真实高度替代固定max-height解决动画适配问题无障碍设计支持键盘操作 焦点反馈符合企业级前端开发规范模式封装将互斥 / 自由展开封装为可切换模式提升代码复用性响应式交互适配点击 / 触摸 / 键盘三种交互方式覆盖全端使用场景可配置化开发提取配置对象降低核心逻辑修改成本符合工程化思维。 互动留言这个企业级手风琴菜单完美适配后台管理系统、移动端侧边栏、官网导航等场景解决了传统手风琴动画不精准、状态不记忆、交互单一的痛点代码可直接复用如需对接后端接口、添加拖拽排序、暗黑模式等扩展功能评论区留言我会第一时间补充完整代码
【JS 实战案例】实现可折叠手风琴菜单(高频 UI 组件)
案例核心效果基础功能点击标题展开 / 折叠内容经典手风琴交互双模式切换互斥展开 / 自由展开适配不同业务场景高度自适应动画非固定 max-height精准匹配内容高度无多余空白 / 截断移动端触摸支持 键盘回车 / 空格触发全端无障碍交互嵌套二级菜单覆盖后台管理系统真实场景本地存储记忆页面刷新保留展开状态体验拉满样式升级分层阴影、hover 动效、过渡曲线优化视觉质感拉满。 实现思路HTML语义化结构使用 details/summary 语义标签兜底兼容无障碍 嵌套层级设计CSS放弃固定 max-height通过 JS 计算内容真实高度实现精准动画自定义 transition-timing-function 实现自然回弹效果响应式适配 触摸交互样式优化JS封装核心函数支持模式切换、状态记忆、嵌套处理监听点击 / 触摸 / 键盘事件全端交互闭环localStorage 持久化展开状态提升用户体验。 完整源码可直接复制运行企业级质量html预览!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0/ titleJS 手风琴折叠菜单 | 实战案例企业级版/title style /* 全局重置基础样式规范且兼容 */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: Microsoft YaHei, PingFang SC, sans-serif; } body { padding: 40px 20px; background: #f5f7fa; color: #333; } .container { max-width: 700px; margin: 0 auto; } .title { text-align: center; margin-bottom: 30px; font-size: 24px; font-weight: 600; color: #2c3e50; } /* 模式切换按钮 */ .mode-switch { text-align: center; margin-bottom: 20px; } .switch-btn { padding: 8px 16px; margin: 0 8px; border: 1px solid #409eff; border-radius: 6px; background: #fff; color: #409eff; cursor: pointer; transition: all 0.2s ease; } .switch-btn.active { background: #409eff; color: #fff; } /* 手风琴容器 */ .accordion { border-radius: 8px; overflow: hidden; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); } /* 手风琴项核心样式升级 */ .accordion-item { background: #fff; border-bottom: 1px solid #e5e6eb; } .accordion-item:last-child { border-bottom: none; } /* 标题栏交互增强 */ .accordion-header { padding: 16px 20px; font-size: 16px; font-weight: 500; cursor: pointer; position: relative; display: flex; align-items: center; transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); user-select: none; } .accordion-header:hover { background-color: #f8f9fa; } .accordion-header:focus { outline: 2px solid rgba(64, 158, 255, 0.3); outline-offset: -2px; } /* 箭头图标动效升级 */ .accordion-icon { width: 16px; height: 16px; margin-right: 12px; border-radius: 2px; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; align-items: center; justify-content: center; } .accordion-icon::after { content: ; width: 6px; height: 6px; border-right: 2px solid #666; border-bottom: 2px solid #666; transform: rotate(45deg); transition: transform 0.3s ease; } .accordion-item.active .accordion-icon::after { transform: rotate(-135deg); } /* 内容区域精准动画核心 */ .accordion-content { overflow: hidden; height: 0; transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1); padding: 0 20px; color: #555; line-height: 1.8; } .accordion-content-inner { padding: 16px 0; } /* 嵌套二级菜单样式 */ .accordion-submenu { margin: 8px 0 8px 24px; border-left: 2px solid #e5e6eb; padding-left: 16px; } .accordion-subitem { padding: 8px 0; font-size: 14px; color: #666; cursor: pointer; transition: color 0.2s ease; } .accordion-subitem:hover { color: #409eff; } /* 移动端适配精细化 */ media (max-width: 768px) { .title { font-size: 20px; } .accordion-header { padding: 14px 16px; font-size: 15px; } .accordion-content { padding: 0 16px; font-size: 14px; } .switch-btn { padding: 6px 12px; font-size: 14px; } } /style /head body div classcontainer h2 classtitle手风琴折叠菜单企业级版/h2 !-- 模式切换按钮 -- div classmode-switch button classswitch-btn active data-modeexclusive互斥展开/button button classswitch-btn data-modefree自由展开/button /div div classaccordion idaccordion !-- 一级菜单1默认展开 -- div classaccordion-item data-keymenu1 div classaccordion-header tabindex0 span classaccordion-icon/span 前端学习路线 /div div classaccordion-content div classaccordion-content-inner 从零基础到高级工程师的完整路线 div classaccordion-submenu div classaccordion-subitem基础阶段HTML → CSS → JavaScript/div div classaccordion-subitem进阶阶段ES6 → 浏览器原理 → 网络基础/div div classaccordion-subitem框架阶段Vue3 → React → 小程序/div div classaccordion-subitem工程化Webpack → Vite → Git → 自动化部署/div /div 核心原则多敲代码多做项目拒绝只看不动手。 /div /div /div !-- 一级菜单2 -- div classaccordion-item data-keymenu2 div classaccordion-header tabindex0 span classaccordion-icon/span JS实战100题计划 /div div classaccordion-content div classaccordion-content-inner 每天1个实战小案例3个月夯实前端核心能力 div classaccordion-submenu div classaccordion-subitem第1-20题DOM基础交互轮播、评分、倒计时/div div classaccordion-subitem第21-50题组件封装手风琴、日历、表单/div div classaccordion-subitem第51-80题异步编程AJAX、Promise、async/await/div div classaccordion-subitem第81-100题工程化性能优化/div /div /div /div /div !-- 一级菜单3 -- div classaccordion-item data-keymenu3 div classaccordion-header tabindex0 span classaccordion-icon/span 面试高频知识点 /div div classaccordion-content div classaccordion-content-inner 前端面试必背核心考点 div classaccordion-submenu div classaccordion-subitemJS核心闭包、原型链、事件循环、防抖节流/div div classaccordion-subitemCSS核心Flex/Grid、BFC、响应式、动画/div div classaccordion-subitem框架核心Vue响应式、React虚拟DOM、生命周期/div div classaccordion-subitem工程化打包优化、模块化、跨域解决方案/div /div /div /div /div !-- 一级菜单4 -- div classaccordion-item data-keymenu4 div classaccordion-header tabindex0 span classaccordion-icon/span 项目实战建议 /div div classaccordion-content div classaccordion-content-inner 从易到难的项目练习路径 div classaccordion-submenu div classaccordion-subitem入门级TodoList、轮播图、计算器/div div classaccordion-subitem进阶级后台管理系统、电商首页、天气应用/div div classaccordion-subitem高级级在线文档、实时聊天、可视化大屏/div /div 每个项目都要做好需求分析 → 技术选型 → 代码实现 → 优化迭代。 /div /div /div /div /div script // 核心配置可配置化体现扩展性 const ACCORDION_CONFIG { defaultActiveKey: menu1, // 默认展开项 storageKey: accordion_active_state, // 本地存储key defaultMode: exclusive // 默认模式exclusive-互斥free-自由 }; // 全局状态 let currentMode ACCORDION_CONFIG.defaultMode; let activeStates JSON.parse(localStorage.getItem(ACCORDION_CONFIG.storageKey)) || {}; // 初始化从本地存储恢复状态 function initAccordion() { const accordion document.getElementById(accordion); const items accordion.querySelectorAll(.accordion-item); // 初始化默认状态 if (Object.keys(activeStates).length 0) { activeStates[ACCORDION_CONFIG.defaultActiveKey] true; } // 渲染初始状态 items.forEach(item { const key item.dataset.key; if (activeStates[key]) { setItemActive(item, true); } }); // 绑定所有标题事件 bindHeaderEvents(); // 绑定模式切换事件 bindModeSwitchEvents(); } // 绑定标题点击/键盘/触摸事件全端交互 function bindHeaderEvents() { const headers document.querySelectorAll(.accordion-header); headers.forEach(header { // 点击事件核心 header.addEventListener(click, () { toggleItem(header.parentElement); }); // 键盘事件无障碍 header.addEventListener(keydown, (e) { if (e.key Enter || e.key ) { e.preventDefault(); toggleItem(header.parentElement); } }); // 触摸事件移动端 header.addEventListener(touchend, (e) { e.preventDefault(); toggleItem(header.parentElement); }); }); } // 绑定模式切换事件 function bindModeSwitchEvents() { const switchBtns document.querySelectorAll(.switch-btn); switchBtns.forEach(btn { btn.addEventListener(click, () { // 更新按钮状态 switchBtns.forEach(b b.classList.remove(active)); btn.classList.add(active); // 更新当前模式 currentMode btn.dataset.mode; }); }); } // 切换菜单项展开/折叠状态核心逻辑升级 function toggleItem(item) { const key item.dataset.key; const isActive item.classList.contains(active); // 互斥模式先关闭所有 if (currentMode exclusive !isActive) { document.querySelectorAll(.accordion-item).forEach(i { setItemActive(i, false); delete activeStates[i.dataset.key]; }); } // 切换当前项状态 setItemActive(item, !isActive); activeStates[key] !isActive; // 持久化到本地存储 localStorage.setItem(ACCORDION_CONFIG.storageKey, JSON.stringify(activeStates)); } // 设置菜单项激活状态精准高度计算核心 function setItemActive(item, isActive) { const content item.querySelector(.accordion-content); const contentInner item.querySelector(.accordion-content-inner); if (isActive) { // 计算内容真实高度实现精准动画 const height contentInner.offsetHeight; content.style.height ${height}px; item.classList.add(active); } else { content.style.height 0px; item.classList.remove(active); } } // 初始化执行 initAccordion(); /script /body /html 核心代码拆解1. HTML 结构语义化 可扩展新增data-key标识每个菜单项用于本地存储状态关联嵌套accordion-submenu实现二级菜单贴合真实业务场景标题添加tabindex0支持键盘聚焦符合无障碍设计规范模式切换按钮独立分区交互逻辑与菜单解耦。2. CSS 核心升级精准动画放弃固定max-height改用height:0JS 计算真实高度彻底解决内容截断 / 空白问题动效质感使用cubic-bezier(0.4, 0, 0.2, 1)自定义过渡曲线动画更自然回弹无障碍优化focus状态添加外轮廓支持键盘操作反馈分层样式通过边框、阴影、间距营造视觉层级符合企业级 UI 设计规范。3. JS 核心逻辑企业级思维✅状态持久化用localStorage存储展开状态页面刷新不丢失设计activeStates对象管理状态结构清晰易维护✅模式切换支持 “互斥展开” 和 “自由展开” 两种模式适配不同场景模式切换按钮独立绑定事件与核心逻辑解耦✅精准高度计算通过offsetHeight获取内容真实高度动画无冗余 / 截断封装setItemActive函数统一管理状态切换✅全端交互支持点击、触摸、键盘Enter / 空格三种触发方式移动端适配精细化不同屏幕尺寸样式自适应✅可配置化提取ACCORDION_CONFIG配置对象修改默认项 / 存储 key 无需改核心逻辑嵌套菜单结构可无限扩展满足复杂业务需求。 新手进阶踩坑点动画卡顿 / 不自然原因使用max-height固定值或默认ease过渡曲线解决方案改用真实高度计算 自定义贝塞尔曲线cubic-bezier(0.4, 0, 0.2, 1)本地存储状态错乱原因未给菜单项加唯一data-key状态关联失败解决方案每个accordion-item添加data-key与activeStates一一绑定键盘操作无响应原因标题无tabindex或未监听keydown事件解决方案添加tabindex0并监听 Enter / 空格键嵌套菜单样式混乱原因未做层级样式隔离解决方案通过margin/padding/border营造嵌套视觉层级避免样式污染。 高级扩展思路懒加载内容展开时异步加载菜单内容优化首屏加载速度拖拽排序结合SortableJS实现菜单拖拽排序存储排序结果权限控制根据用户权限动态显示 / 隐藏菜单项动画扩展添加内容淡入淡出 标题颜色渐变视觉效果更丰富API 联动对接后端接口动态渲染菜单列表支持增删改查暗黑模式添加暗黑模式切换适配不同主题风格。 核心知识点总结状态管理本地存储 内存状态双管理实现状态持久化与实时更新精准动画通过offsetHeight计算真实高度替代固定max-height解决动画适配问题无障碍设计支持键盘操作 焦点反馈符合企业级前端开发规范模式封装将互斥 / 自由展开封装为可切换模式提升代码复用性响应式交互适配点击 / 触摸 / 键盘三种交互方式覆盖全端使用场景可配置化开发提取配置对象降低核心逻辑修改成本符合工程化思维。 互动留言这个企业级手风琴菜单完美适配后台管理系统、移动端侧边栏、官网导航等场景解决了传统手风琴动画不精准、状态不记忆、交互单一的痛点代码可直接复用如需对接后端接口、添加拖拽排序、暗黑模式等扩展功能评论区留言我会第一时间补充完整代码