rolling penetration
Problem Description
In the development of mobile WEB (the applet is also the same), as shown in the above screen recording, if a scroll bar appears when the page exceeds one screen height, slide on the fixed-positioned pop-up mask layer, and the content below it will also be displayed. Following the scrolling, it looks as if the event penetrates to the underlying DOM element, let's call it scroll penetration.
problem causes
It can be guessed that the scroll event of the document (document) is triggered, if you can disable the scroll event, it will be easier.
Case Pseudocode
<div class="btn">点击出现弹窗</div>
<div class="popup">
<div class="popup-mask"></div>
<div class="popup-body popup-bottom">
<div class="header">我是标题</div>
<div class="content">
<div>0</div>
<div>1</div>
<div>...</div>
</div>
</div>
</div>
.popup-mask {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
z-index: 998;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.popup-body {
padding: 0 50px 40px;
background-color: #fff;
position: fixed;
z-index: 999;
}
✅ Solution A (touch-action)
By default, pan (scroll) and zoom gestures are handled exclusively by the browser, but the behavior of touch gestures can be changed through the CSS property touch-action. Extract the values of several touch-actions as follows.
value | describe |
---|---|
auto | Enables the browser to handle all pan and zoom gestures. |
none | Disables browser handling of all pan and zoom gestures. |
manipulation | Enable pan and zoom gestures, but disable other non-standard gestures, such as double-tap to zoom. |
pinch-zoom | Enables multi-finger panning and zooming of the page. |
So setting this property on the popup element and disabling all gestures on the element (and its non-scrollable descendants) solved the problem.
.popup {
touch-action: none;
}
Note: [Accessibility Design] Preventing page zooming may prevent people with poor eyesight from reading and comprehending page content, but the applet itself doesn't seem to be zoomable!
✅ Solution B (event.preventDefault)
A standard from the W3C.
To the effect, calling the preventDefault method on the touchstart and touchmove events prevents the default behavior of any associated events, including mouse events and scrolling.
So we can handle it like this.
Step 1. Listen to the touchmove event of the popup's outermost element (popup) and prevent the default behavior to disable all scrolling (including scrolling elements inside the popup).
Step 2. Release the scrolling element in the pop-up window and allow it to scroll: also monitor the touchmove event, but prevent the bubbling behavior (stopPropagation) of the scrolling element, so that the outermost element (popup) cannot receive the touchmove event when scrolling .
const popup = document.querySelector('.popup')
const scrollBox = document.querySelector('.content')
popup.addEventListener('touchmove', (e) => {
// Step 1: 阻止默认事件
e.preventDefault()
})
scrollBox.addEventListener('touchmove', (e) => {
// Step 2: 阻止冒泡
e.stopPropagation()
})
scroll overflow
Problem Description
As shown in the above screen recording, the pop-up window also contains scrolling elements. When the scrolling element scrolls to the bottom or top, scrolling down or up will also trigger the scrolling of the page. This phenomenon is called scroll chain ( scroll chaining), but the name overscroll feels more eloquent.
❌ Solution A (overscroll-behavior)
overscroll-behavior is a CSS feature that allows to control the behavior of the browser scrolling to the border. It has the following values.
value | describe |
---|---|
auto | By default, scrolling of an element is propagated to ancestor elements. |
contain | Blocks the scroll chain, scrolling is not propagated to ancestor elements, but local effects of the node itself are displayed. For example the glow effect for overscrolling on Android or the rubber band effect on iOS. |
none | Same as contain, but blocks its own over-effects. |
So the problem can be solved like this:
.content {
overscroll-behavior: none;
}
Simple, clean and high-performance, but Safari does not support the entire system, the compatibility is as follows, do you feel that Safari is a modern version of IE (I heard from passers-by by chance)!
✅ Solution B (event.preventDefault)
Borrowing the ability of event.preventDefault, when the component scrolls to the bottom or top, all scrolling is blocked by calling event.preventDefault, so that page scrolling will not be triggered, and no processing will be done between scrolling.
let initialPageY = 0
scrollBox.addEventListener('touchstart', (e) => {
initialPageY = e.changedTouches[0].pageY
})
scrollBox.addEventListener('touchmove', (e) => {
const deltaY = e.changedTouches[0].pageY - initialPageY
// 禁止向上滚动溢出
if (e.cancelable && deltaY > 0 && scrollBox.scrollTop <= 0) {
e.preventDefault()
}
// 禁止向下滚动溢出
if (
e.cancelable &&
deltaY < 0 &&
scrollBox.scrollTop + scrollBox.clientHeight >= scrollBox.scrollHeight
) {
e.preventDefault()
}
})
Complete solution demo
https://github.com/Barrior/cases/blob/main/overscroll.html#L107-L143
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。