今天写的某个手机页面需要一个悬浮弹窗,为了使该弹窗更加显眼,于是我就给页面流之上添加了一个黑色半透明的遮罩div对下层页面进行了覆盖,之后将弹窗层放在最上方。

但令我没想到的是,在移动端仍然能透过这个遮罩层去滑动下层页面文档流,查了一下,原来是HTML中元素默认点击穿透,阻止穿透的话,可以添加css属性 pointer-events: none;,但我给遮罩层添加上这个属性之后,依然不能阻止透过它对下层页面进行滑动的现象,只是禁掉了它上面的点击事件,滚动效果依然没有禁止。

ZSL学长和保罗都想到了一个方法,就是:在弹窗和遮罩层出来的时候,给body加 overflow:hidden;,这样页面溢出部分隐藏掉就没法滚动了;在弹窗关闭的时候,清除掉该属性,恢复页面的长度使其可以滚动。显然,这是一个非常简单且容易理解的实现方法。

但是我发现还有另外一种实现方式,就是给html和body加上 overflow:auto; 属性,也能够实现我想要的效果:没弹窗时页面正常滚动,有弹窗遮罩层时无法穿透遮罩层对下方页面滚动。

用下面的小 Demo 进行代码复现,其中我将遮罩层的高度调整为了页面高度的一半,方便进行滚动效果对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,body {
margin: 0;
padding: 0;
height: 100%;
position: relative;
/* 防止鼠标透过遮罩层滚动下层页面 */
overflow: auto;
}
.mask {
height: 50%;
width: 100%;
background-color: rgba(0, 0, 0, 0.589);
position: fixed;
top: 0;
}
</style>
</head>
<body>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
噫吁嚱,危乎高哉!<br>
蜀道之难,难于上青天!<br>
蚕丛及鱼凫,开国何茫然!<br>
尔来四万八千岁,不与秦塞通人烟。<br>
西当太白有鸟道,可以横绝峨眉巅。<br>
地崩山摧壮士死,然后天梯石栈相钩连。<br>
上有六龙回日之高标,下有冲波逆折之回川。<br>
黄鹤之飞尚不得过,猿猱欲度愁攀援。<br>
青泥何盘盘,百步九折萦岩峦。<br>
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。<br>
但见悲鸟号古木,雄飞雌从绕林间。<br>
又闻子规啼夜月,愁空山。<br>
蜀道之难,难于上青天,使人听此凋朱颜!<br>
连峰去天不盈尺,枯松倒挂倚绝壁。<br>
飞湍瀑流争喧豗,砯崖转石万壑雷。<br>
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。<br>
所守或匪亲,化为狼与豺。<br>
朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。<br>
锦城虽云乐,不如早还家。<br>
蜀道之难,难于上青天,侧身西望长咨嗟!<br>
<div class="mask"></div>
</body>
</html>

可以发现,在html和body存在 overflow: auto; 时,可以防止PC端鼠标滚轮(或移动端滑动触屏)透过遮罩层滚动下层页面;而当把该属性注释掉,PC端鼠标滚轮(或移动端滑动触屏)就可以透过该遮罩div直接对下层页面进行滚动。

正当我百思不得其解时,和保罗聊了几句,让我意识到了:

由于我给html和body加了高度100%,因此现在该页面的高度不是由内容撑开的了,而是固定值,即浏览器窗口的高度。页面文档流之所以看起来可以滚动,是因为页面内容溢出body,滚动的其实是溢出的内容而不是body的文档流,body早就一开始被滚动到上面去了。
当给html和body加上 overflow: hidden; 或者 overflow: auto; 等属性时,html和body才会变成一个正常的无溢出的滚动容器,高度虽然还是固定值,即浏览器窗口的高度,但body不会随着文档流被滚动到上面。因为此时页面的滚动效果是真的滚动,滚动的是作为容器的body里面的内容。

但是我还是想知道原理,为什么html和body存在 overflow: auto; 时,它会使穿透失效,为什么不能直接控制滚动下层元素了呢?

直到我发现了:html和body元素存在 overflow: auto; 时,遮罩层竟然遮挡覆盖了页面竖直滚动条!此时PC端鼠标滚轮(和移动端滑动触屏)都不能透过遮罩div进行滑动。

遮罩层覆盖了页面竖直滚动条

当把html和body的 overflow: auto; 注释掉时,遮罩层就不遮挡页面滚动条了,此时PC端鼠标滚轮(和移动端滑动触屏)又都可以透过遮罩div滑动下层页面了。

遮罩层未覆盖页面滚动条


经过我的验证,起作用的其实是html标签的overflow: auto;属性,而不是body的,因此可以这样理解:

  • 当html高度固定且存在 overflow: auto; 时,整个html页面是个滚动容器,滑动条也是这个容器的一部分,遮罩层完全遮挡和覆盖了整个html(当然也包括其中的body元素),因此无法透过遮罩对文档流进行任何滚动。
  • 当html高度固定但没有 overflow: auto; 时,页面中超出内容会溢出,具有固定高度的html元素(和它里面的body元素会随着页面滚动到上面),此时鼠标滚轮(或移动端触屏滑动)造成的滚动效果其实是溢出内容引起的,html是正常页面流而不是一个滚动容器,遮罩层盖住的是页面中body里的内容,但没遮住整个html和html页面的滚动条,所以可以通过遮罩div直接穿透滑动下方页面内容。

感谢保罗的答疑解惑,虽然……Emm…我还是不太懂遮罩层覆盖住html和它的竖直滚动条后,怎么就不穿透了?但保罗说没必要琢磨这么多,我也就不钻这牛角尖了,知道效果如何就行了。

另外保罗推荐给我了一个“取消滚动链接”的CSS属性 overscroll-behavior 让我玩玩。

Xi4GFJ.jpg