details和summary原生支持无JS折叠展开,summary必须为details的第一个子元素;open为布尔属性,存在即默认展开;点击或键盘Enter/Space触发切换,支持可访问性;样式定制需用list-style: none加::before重绘箭头,避免破坏语义与交互。

用 details 和 summary 最快实现通知折叠展开
原生 HTML 就支持,不用 JS 也能做基础折叠,适合系统通知列表这种「默认收起、点击展开详情」的场景。details 是语义化容器,summary 是可点击的标题栏,浏览器自动处理展开/收起状态和箭头图标。
常见错误是把 summary 写成多个标签或加 div 包裹——它必须是 details 的**第一个子元素**,且只能有一个:
<details><summary>新消息(3)</summary><p>你有 3 条未读系统通知</p>
<ul>
<li>密码策略已更新</li>
<li>API 访问配额剩余 12%</li>
</ul></details>
- 默认收起,点击
summary文本区域(含空白)即可展开 - 支持键盘操作:
Enter或Space键触发 - 不支持嵌套
details(部分浏览器会失效) - 移动端点击区域小,建议给
summary加padding扩大热区
需要 JS 控制展开状态时,操作 open 属性
原生 details 的展开状态由 open 布尔属性控制。JS 可读写它,但注意:直接修改属性不会触发动画,也不会同步到 DOM 属性值(仅影响实际状态)。
典型使用场景:清空通知后自动收起、点击其他通知项时关闭之前展开的项:
const items = document.querySelectorAll('details.notification-item');
items.forEach(item => {
item.addEventListener('toggle', () => {
// 展开/收起时触发,比 click 更准确
if (item.open) {
console.log('当前展开的是:', item.dataset.id);
}
});
});
- 用
item.open = true/false设置状态,不要用item.setAttribute('open', '') -
toggle事件比click更可靠——用户用键盘操作也会触发 - 想默认展开?直接在 HTML 中写
,别用 JS 模拟 - 服务端渲染时慎用
open:可能造成水合 mismatch(React/Vue SSR 场景)
样式定制要绕过浏览器默认箭头
各浏览器对 summary 前的三角箭头实现不同,不能用 ::before 覆盖。正确方式是隐藏原生箭头,再用伪元素重绘:
details summary {
list-style: none;
}
details summary::marker {
content: ''; /* Chrome/Firefox 110+ 支持 */
}
details summary::before {
content: '▶ ';
margin-right: 4px;
transition: transform 0.2s;
}
details[open] summary::before {
transform: rotate(90deg);
}
-
list-style: none清除旧版浏览器箭头 -
::marker是现代标准,但 Safari 目前不支持,需降级方案 - 用
details[open]选择器比监听 class 更稳妥(无 JS 时也生效) - 避免给
summary设display: flex后丢失点击响应——加cursor: pointer并确保子元素没阻止事件冒泡
和通知标记已读逻辑耦合时的坑
系统通知常需「展开即标记为已读」,但 toggle 事件在收起时也会触发,容易误标。必须区分方向:
- 只在展开时调用标记接口:
if (event.target.open) { markAsRead(id); } - 避免在
summary上额外绑click—— 会和原生行为冲突,导致双触发 - 如果通知内容含链接或按钮,记得
pointer-events: none给非交互区域,防止误点收起 - 批量操作(如「全部标记为已读」)后,别直接移除
details元素——会导致已展开项突然消失,体验突兀;应保留 DOM,仅更新文本和状态
真正难的不是折叠本身,而是状态同步:展开动作是否代表用户「查看」、是否要上报埋点、是否影响未读数计算——这些逻辑得和业务层对齐,HTML 层只管呈现。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











