场景:切换手风琴时内容乱跳
手风琴里塞了大量内容时有个经典 UX 问题:用户已经滚过了某个条目,点击展开下一个条目时,上一个条目收起导致页面高度骤减,内容整体向上窜,用户可能还得往回滚才能看到新展开内容的开头。
这不是 Bricks 的 Bug,也不是手风琴元素的缺陷。去搜「accordion scroll」,会发现 Webflow、Bootstrap、几乎所有带手风琴的 WordPress 主题用户都在问同一个问题。浏览器为了保持滚动位置,在页面高度变化时会让内容上移——这是浏览器的既定行为,不是故障。
先想清楚:手风琴适合放什么内容
动手写代码前,先退一步看本质。手风琴的设计意图是把内容压缩到零高度,适合短列表(比如 FAQ 问答列表:用户快速扫过问题、点开感兴趣的答案)。它不适合用来隐藏整段大内容——条目收起时页面高度大幅变化,就会引发上面的跳动问题。
大多数手风琴元素都提供了「展开一个时不要收起其他」的选项来规避此问题——在 Bricks 里是 Independent toggle(独立切换)设置,打开后多个条目可以同时展开,页面高度不再突变。
所以第一步不是写 JS,而是确认需求:内容很长、又必须用收起式手风琴、且旧条目必须关闭——只有在这种情况下才需要本文的滚动方案。换个思路的话,用 Tabs(标题始终固定在顶部)或 Modal/Lightbox(内容在弹层里打开)也能解决问题,而且完全不需要处理滚动。
方案:展开完成后自动滚动到条目
如果确实必须用手风琴,就在 Bricks 的 页面设置 → 自定义代码 → 页脚脚本 里粘贴以下 JS(原样保留,注意放在 script 标签内)。此方案针对 Bricks 的 Nestable 手风琴元素,Query Loop 输出手风琴的场景同样适用:
document.addEventListener("DOMContentLoaded", () => {
const accordionHeader = '.accordion-title-wrapper';
const delay = 200;
const offset = 200;
document.querySelectorAll(accordionHeader).forEach((header) => {
header.addEventListener('click', () => {
setTimeout(() => {
window.scrollTo({
top: header.getBoundingClientRect().top + window.pageYOffset - offset,
behavior: 'smooth'
});
}, delay);
})
})
})
逻辑:点击任意手风琴标题后,等待 delay(200ms,留给展开动画),然后计算该标题的绝对页面位置(getBoundingClientRect().top + window.pageYOffset),减去 offset(200px)留出视觉余量,平滑滚动过去。这样用户点击后,视线会自动落在新展开条目的标题处。
原理说明
- 为什么要点击后延迟再滚:手风琴展开有动画(Bricks 默认高度过渡),如果立即滚动,动画还没跑完、目标位置仍在变化,会滚错地方。200ms 让动画基本完成后再计算位置。
- 坐标计算:
getBoundingClientRect().top是元素相对视口顶部的距离,window.pageYOffset是当前滚动距离,两者相加即元素在文档中的绝对 Y 坐标;scrollTo支持behavior: 'smooth'平滑滚动。 - offset 的作用:直接滚到标题顶部会让标题贴着浏览器上边缘,视觉上憋屈;留 200px 余量(也可以理解为吸顶偏移),阅读体验更好。
- 为什么这不是治本:滚动只是「事后补救」,页面高度突变的问题依然存在。真要治本,要么换 Tabs/Modal 方案,要么打开 Independent toggle 让条目并存。
小结
手风琴内容跳动是浏览器保持滚动位置的必然结果,不是 Bug。正确顺序是:先判断组件选型(短内容用 Accordion、长内容考虑 Tabs/Modal)、必要时开 Independent toggle、最后才用 JS 自动滚动兜底。这套「先想清楚再动手」的思路,比直接抄代码更有价值。