是浏览器原生支持的展开收起方案,语义正确、无需 JS、自带可访问性;需用 summary::marker 定制箭头,监听 toggle 事件响应状态变化,并注意动态内容防重复加载。

用 <details></details> 和 <summary></summary> 做原生展开收起最省事
浏览器原生支持的 <details></details> 标签就是干这个的,不用 JS、不依赖框架,语义正确,还自带 ARIA 属性和键盘可访问性(空格/回车切换)。它不是“模拟”,而是标准方案。
常见错误是直接套用 <div> + JS 手动控制 display 或 height,结果破坏可访问性、增加维护成本、移动端点按延迟。<ul>
<li>
<code><summary></summary> 必须是 <details></details> 的第一个子元素,否则无法触发交互
open 属性:<details open></details>
<summary></summary> 里可以放文字、图标甚至 <button></button>,但别再嵌套另一个 <details></details>(会触发多重事件)<details><summary>点击展开更多内容</summary><p>这里是隐藏的详细说明……</p> </details>
样式定制必须重置 summary::marker 才能改箭头
默认的下拉箭头是伪元素 summary::marker,直接写 summary { background: #eee; } 不会影响它。想换图标、隐藏箭头、对齐文字,必须显式处理这个伪元素。
- 隐藏原生箭头:
summary::marker { content: ""; } - 用 Unicode 替换:
summary::marker { content: "▶"; }(注意 open 状态需配合 JS 切换) - 用背景图或 SVG 图标时,
summary::marker要设为display: none,再给summary加padding-left预留空间 - Chrome 和 Safari 对
::marker支持较好,Firefox 旧版本可能需要list-style: none+display: flex兜底
需要监听展开/收起状态?用 toggle 事件,别查 open 属性
<details></details> 触发状态变化时会派发 toggle 事件,比轮询 element.open 或监听 click 更可靠——比如用户用键盘操作、屏幕阅读器切换,或者 JS 调用 element.open = true,都会触发它。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 监听写法:
detailsEl.addEventListener('toggle', () => console.log(detailsEl.open)) - 不要在事件回调里立刻读
detailsEl.open后立即修改 DOM(如插入内容),某些浏览器有微任务延迟,建议用queueMicrotask包一层 - 如果要兼容 IE(已无必要),得退回到自定义组件;现代项目中,直接用
toggle事件是最轻量且健壮的选择
动态加载内容时,toggle 事件里插入 HTML 要防重复渲染
很多场景是点击后才请求接口、渲染详情,这时容易在多次展开/收起中反复 fetch 和 innerHTML,既浪费资源又可能导致状态错乱。
- 用布尔变量标记是否已加载,例如
if (!detailsEl.dataset.loaded) { /* fetch + render */ detailsEl.dataset.loaded = 'true'; } - 避免直接拼接 HTML 字符串,优先用
template或document.createElement构建节点,防止 XSS(尤其内容来自 API) - 如果内容含脚本或第三方组件(如图表),别用
innerHTML,改用replaceChildren()或手动挂载逻辑
细节往往卡在状态管理上:原生标签很稳,但人容易在“加载中”“已加载”“出错重试”这些分支里漏掉边界情况。










