应通过绑定状态统一控制符号切换,推荐用textContent或独立span更新,结合details的toggle事件确保与open状态同步,并同步更新aria-expanded属性以保障可访问性。
直接用 JavaScript 切换按钮文本最简单,但容易忽略状态同步问题。关键是把展开/折叠状态和符号绑定到同一来源,避免手动维护两套状态。
innerHTML = '+' / innerHTML = '-' 硬切——如果按钮里还有其他 HTML(比如图标、空格),会意外清掉textContent 更新纯文本,或给符号单独加一个 <span class="toggle-icon"> 容器,只改它不能直接靠 HTML 原生实现——summary 里写死的 + 不会自动变 -。但可以用 CSS 配合 :has() 模拟(现代浏览器支持):
summary::after {content: '+';}details[open] summary::after {content: '-';}
注意::has() 在 Safari 15.4+ 和 Chrome 105+ 才稳定支持;IE 完全不支持;若需兼容旧版,必须用 JS。
常见原因是事件监听重复绑定,或者没区分「点击 summary」和「点击内部按钮」——如果 summary 里嵌了 button,又给 button 和 summary 都绑了 click,就会触发两次。
details 的 toggle 事件,比监听 click 更可靠:它只在实际展开/折叠后触发event.target.open,而不是查 details.hasAttribute('open')(可能滞后)toggle 回调里,确保与 DOM 状态严格一致可以,但没必要过度设计。纯字符更轻量、可访问性更好(屏幕阅读器能自然读出“加号”“减号”),且无需额外加载资源。
aria-hidden="true",并在 summary 文本里保留语义描述,比如 折叠详情(当前已展开)
+/- 字符天然随字号缩放,SVG 需额外设 width/height 或用 em 单位真正容易被忽略的是:符号切换必须和 aria-expanded 同步更新——哪怕视觉上只是个加减号,辅助技术依赖这个属性判断状态。漏掉它,对屏幕阅读器用户就是不可用的。