Scroll penetration and scroll overflow

Barrior
中文

Roll through

滚动穿透.gif

Problem Description

During the development of the mobile web (small programs are the same), as shown in the screen recording above, if a scroll bar appears when the page is more than one screen high, slide on the pop-up mask layer of the fixed location, and the content below it will also be Scrolling together, it looks as if the event penetrates the DOM element below, let's call it scroll penetration.

problem causes

It can be guessed that the scroll event of the document was triggered. It would be easier if the scroll event could be disabled.

Case pseudo code

<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, panning (scrolling) and zooming gestures are handled exclusively by the browser, but the behavior of touch gestures can be changed through the CSS feature touch-action. Extract the values of several touch-actions as follows.

valuedescribe
autoEnable the browser to handle all pan and zoom gestures.
noneDisable the browser from processing all pan and zoom gestures.
manipulationEnable pan and zoom gestures, but disable other non-standard gestures, such as double-tap to zoom.
pinch-zoomEnable multi-finger panning and zooming of the page.

So setting this attribute on the popup element and disabling all gestures on the element (and its non-scrollable descendants) can solve the problem.

.popup {
  touch-action: none;
}

Note: [Accessibility Design] Preventing page zoom may affect people with low vision to read and understand the content of the page, but the applet itself seems to be unable to zoom!

✅ Solution B (event.preventDefault)

standard from W3C.

描述.jpg

The general idea is that calling the preventDefault method in the touchstart and touchmove events can prevent the default behavior of any associated events, including mouse events and scrolling.

So we can deal with it like this.
Step 1. Listen to the touchmove event of the outermost element (popup) of the popup and prevent the default behavior from disabling all scrolling (including scrolling elements inside the popup).
Step 2. Release the scroll element in the pop-up window and allow it to scroll: also monitor the touchmove event, but prevent the bubbling behavior of the scroll element (stopPropagation), 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

滚动溢出.gif

Problem Description

As shown in the screen recording above, the pop-up window also contains scrolling elements. When the scrolling element rolls 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 expressive.

❌ Solution A (overscroll-behavior)

overscroll-behavior is a feature of CSS that allows to control the behavior of the browser scrolling to the border. It has the following values.

valuedescribe
autoThe default effect, the scrolling of the element can be propagated to the ancestor element.
containPrevent the scrolling chain, scrolling will not propagate to the ancestor element, but will show the local effect of the node itself. For example, the over-scrolling glow effect or the rubber band effect on iOS.
noneSame as contain, but prevents its own excessive effects.

So the problem can be solved like this:

.content {
  overscroll-behavior: none;
}

Concise, clean and high-performance, but Safari does not support the whole system, the compatibility is as follows, do you feel that Safari is the modern version of IE (accidentally heard by passers-by)!
兼容性.jpg

✅ Solution B (event.preventDefault)

Borrowing the ability of event.preventDefault, when the component scrolls to the bottom or top, all scrolling is prevented by calling event.preventDefault, so that page scrolling will not be triggered, and no processing is 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()
    }
})

Solution complete Demo

https://github.com/Barrior/cases/blob/main/overscroll.html#L107-L143

阅读 931

技术博客
Barrior's technology articles

开源项目 JParticles 作者

540 声望
54 粉丝
0 条评论
你知道吗?

开源项目 JParticles 作者

540 声望
54 粉丝
宣传栏