局部滚动容器需显式设 overscroll-behavior: contain 于真实滚动容器(如 max-height + overflow-y: auto 的 div),禁用滚动链;iOS 需 JS 锁 viewport 并保存/恢复 pageYOffset,避免 body overflow 失效。

局部滚动容器本身不会自动防穿透,必须显式隔离滚动上下文,否则滚动到底/顶时事件会冒泡到父级甚至 body —— 这就是穿透的根源。
为什么 overflow-y: auto 容器还会穿透
浏览器默认启用滚动链(scroll chaining):当一个可滚动元素滚到边界(scrollTop === 0 或 scrollTop === scrollHeight - clientHeight),继续滑动会把剩余滚动量“传给”最近的祖先可滚动容器。哪怕你只给 .scrollable 设了 overflow-y: auto,只要它的父级(比如 body)也可滚,穿透就必然发生。
- 常见错误:只设
max-height和overflow-y: auto,却没加overscroll-behavior: contain - 更隐蔽的坑:容器是
display: flex子项,但没写min-height: 0,导致子内容撑高、overflow失效,容器实际不可滚 → 属性直接被忽略 - iOS Safari 15–16 中,若容器祖先有
transform或will-change,overscroll-behavior可能静默失效
overscroll-behavior: contain 必须加在谁身上
只能加在「真实发生滚动」的容器上,不是弹窗外壳,不是 body,也不是表格元素 —— 它必须是一个显式设置了 height 或 max-height 且 overflow-y: auto 的块级 div。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 正确位置:
.modal__content { max-height: 70vh; overflow-y: auto; overscroll-behavior: contain; } - 错误写法:
.modal { overscroll-behavior: contain; }(.modal是position: fixed,不构成滚动上下文) - 表格场景必须套一层:
<div class="table-scroller"><table>...</table></div>,然后把属性加在.table-scroller上 - 别用
none:它硬截断,iOS 橡皮筋消失,用户拉到边容易误判为卡死;contain保留弹性反馈,同时阻断传播
移动端兜底必须用 JS 锁 body,但不能只靠 overflow: hidden
document.body.style.overflow = 'hidden' 在 iOS Safari 和多数安卓 WebView 中完全无效 —— 浏览器根本不响应 body 层级的溢出控制,滚动仍走 viewport 级路径。
立即学习“前端免费学习笔记(深入)”;
- 真正起效的是:
document.documentElement.style.position = 'fixed'; document.documentElement.style.top = `-${window.pageYOffset}px`; - 必须同步保存
window.pageYOffset(别用document.body.scrollTop,iOS 下常为 0) - 关闭时用
requestAnimationFrame(() => window.scrollTo(0, savedScrollY)),否则 iOS 下可能闪一下 - 如果弹窗里有可滚动区域(如长列表),JS 拦截
touchmove时必须判断e.target.closest('.scrollable'),只在非可滚动区调e.preventDefault()
最易被忽略的一点:DOM 嵌套过深(比如超过 6 层)会让 touchstart 到 handler 执行延迟显著增加,导致滚动判定滞后、穿透概率上升 —— 不是 CSS 写得不对,是结构本身让事件流变慢了。


















