问题
对一个标签进行fixed定位时候并设置overflow:scroll时,当该元素可以滚动到边界时候,滚动行为会穿透背后元素直至body均会随之滚动
原因分析
当触及页面顶部或者底部时(或者是其他可滚动区域),移动端浏览器倾向于提供一种“触底”效果,甚至进行页面刷新。你可能也发现了,当对话框中含有可滚动内容时,一旦滚动至对话框的边界,对话框下方的页面内容也开始滚动了——这被称为“滚动链”,这是一种浏览器行为。
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.div {
height: 400px;
background: red;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.div:nth-child(2) {
background: green;
}
.div:nth-child(3) {
background: blue;
}
.div:nth-child(4) {
background: yellow;
}
.fixed {
position: fixed;
right: 0;
z-index: 10;
left: 0;
bottom: 0;
height: 300px;
overflow: scroll;
background: #fff;
}
.bg {
position: fixed;
z-index: 0;
right: 0;
top: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="div">1</div>
<div class="div">2</div>
<div class="div">3</div>
<div class="div">4</div>
<div class="bg"></div>
<div class="fixed">
<div style="height:800px;">test</div>
</div>
</body>
</html>
解决办法一(推荐)
利用css属性overscroll-behavior
.fixed {
overflow: auto;
overscroll-behavior: contain; /* 阻止外部滚动 */
}
解决办法二
js边界判断
const scrollElement = document.querySelector('.fixed');
scrollElement.addEventListener('touchstart', (event) => {
const scrollTop = scrollElement.scrollTop;
const scrollHeight = scrollElement.scrollHeight;
const offsetHeight = scrollElement.offsetHeight;
const atTop = scrollTop === 0;
const atBottom = scrollTop + offsetHeight === scrollHeight;
if (atTop) {
scrollElement.scrollTop = 1; // 防止触发背景滚动
} else if (atBottom) {
scrollElement.scrollTop = scrollTop - 1; // 防止触发背景滚动
}
});
scrollElement.addEventListener('touchmove', (event) => {
const scrollTop = scrollElement.scrollTop;
const scrollHeight = scrollElement.scrollHeight;
const offsetHeight = scrollElement.offsetHeight;
if (scrollTop === 0 && event.touches[0].clientY > 0) {
event.preventDefault(); // 防止向上滚动
} else if (scrollTop + offsetHeight === scrollHeight && event.touches[0].clientY < 0) {
event.preventDefault(); // 防止向下滚动
}
}, { passive: false });
解决办法三
使用成熟的库如 body-scroll-lock,可以更优雅地解决滚动穿透问题。
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
const scrollElement = document.querySelector('.scroll-container');
// 禁用背景滚动
disableBodyScroll(scrollElement);
// 恢复背景滚动
enableBodyScroll(scrollElement);