滚动穿透的现象
在开发中我们可以观察到这么一种现象,当用户在屏幕上某个可滚动区域滚动时,如果滚动到当前区域滚动边界时会带动其他区域进行滚动,直观表现是在进行博客评论时,滑动textarea会带动博客一起滑动,另一种是屏幕有一个浮窗,滑动浮窗会导致浮窗之下的元素进行滚动
其中一种情况,为浮窗时
第二种情况,滚动区域本身未脱离文档流
scroll chain
这种子元素会带动其包含块元素进行滚动的形象被称为滚动链,W3C中明确:滚动容器的滚动位置达到滚动边界即可滚动区域的末端时会导致其他内容或整个页面滚动,这种持续滚动的体验被称为滚动链
在博客上评论时,你可能会注意到,如果你的评论超出了提供的textarea的长度,向文本区域末尾滚动会导致整个博客滚动。这是因为达到可滚动区域的末端(称为滚动边界)会导致其他内容或整个页面滚动。这种持续滚动的体验被称为滚动链
解决方法
知道了为什么会触发包含块元素的滚动之后我们就可以通过中断滚动链的形式来解决这个问题,
我们以场景1为例,以下是初始代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>内滚动触发外滚动示例</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.modal {
width: 100%;
background: rgba(0, 0, 0, 0.1);
overflow: hidden;
overflow-y: scroll;
}
.content {
position: absolute;
top: 0;
left: 0;
background: white;
width: 80%;
height: 50vh;
overflow: hidden;
overflow-y: scroll;
}
.show {
display: block;
}
</style>
</head>
<body>
<div class="modal">
<p>这里是外部内容...1</p>
<p>这里是外部内容...2</p>
<p>这里是外部内容...3</p>
<p>这里是外部内容...4</p>
<p>这里是外部内容...5</p>
<p>这里是外部内容...6</p>
<p>这里是外部内容...7</p>
<p>这里是外部内容...8</p>
<p>这里是外部内容...9</p>
<p>这里是外部内容...10</p>
<p>这里是外部内容...11</p>
<p>这里是外部内容...12</p>
<p>这里是外部内容...13</p>
<p>这里是外部内容...14</p>
<p>这里是外部内容...15</p>
<p>这里是外部内容...1</p>
<p>这里是外部内容...2</p>
<p>这里是外部内容...3</p>
<p>这里是外部内容...4</p>
<p>这里是外部内容...5</p>
<p>这里是外部内容...6</p>
<p>这里是外部内容...7</p>
<p>这里是外部内容...8</p>
<p>这里是外部内容...9</p>
<p>这里是外部内容...10</p>
<p>这里是外部内容...11</p>
<p>这里是外部内容...12</p>
<p>这里是外部内容...13</p>
<p>这里是外部内容...14</p>
<p>这里是外部内容...15</p>
<p>这里是外部内容...1</p>
<p>这里是外部内容...2</p>
<p>这里是外部内容...3</p>
<p>这里是外部内容...4</p>
<p>这里是外部内容...5</p>
<p>这里是外部内容...6</p>
<p>这里是外部内容...7</p>
<p>这里是外部内容...8</p>
<p>这里是外部内容...9</p>
<p>这里是外部内容...10</p>
<p>这里是外部内容...11</p>
<p>这里是外部内容...12</p>
<p>这里是外部内容...13</p>
<p>这里是外部内容...14</p>
<p>这里是外部内容...15</p>
</div>
<div class="content">
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
<p>这里是弹层内容...</p>
</div>
</body>
</html>
使包含块本身不可滚动
我们可以尝试使包含块本身变得不可被滚动,使滚动链失效,通过overflow:hidden实现
当前浮窗的包含块元素为body,所以我们给body添加css
body {
overflow: hidden;
}
可以看到此时滚动浮窗时其下方的元素没有跟随浮窗一起滚动
之后我们可以通过控制浮窗是否出现来动态的实现给包含块元素添加css样式
关于滚动链的传播范围
我们可能会有一个疑问,如果在多个滚动区域进行嵌套,只对当前需要进行限制的滚动区域的包含块元素进行限制能否阻断其更上层的滚动链
这是一个简单的三层滚动区域,我们现在对中间部分尝试阻断滚动链
可以发现滚动链越过了中间的滚动区域,直接达到了最外层
那我们就可以合理认为滚动链会从目标元素一直链接到根元素,如果我们仅仅只在当前滚动区域的包含块中阻止滚动链的话是远远不够,而且这也并不是真正意义上的阻止滚动链,那我们该如何真正阻止滚动链呢
overscroll-behavior
overscroll-behavior是一个新的css属性,它专门用来规定当前元素在滚动到滚动边界时的表现状态,具体有三个属性值
- auto
默认值 - contain
设置了这个值之后默认的下拉,上拉的浏览器效果并不会消失,但其包含块以及更上级的滚动链将会被阻止 - none
即阻止滚动链也阻止下拉上拉的浏览器效果
兼容性
兼容性如下:
最终效果
最终效果如下: