1. body-scroll-lock
  2. 原理
  3. 代码
  4. 定位元素下的滚动穿透
  5. 参考

阻止滚动穿透

当滚动元素滚动到顶/底时, 如果其外层还有可滚动的元素,则会触发外层可滚动元素的滚动.
a.gif

这是个feature, 但有时这个feature会给开发者带来麻烦.
特别是safari, 如果某个可滚动元素设置了-webkit-overflow-scrolling: touch,滑动到头的时候经常会被卡住.

body-scroll-lock

用body-scroll-lock可以解决这个问题.当滚动到元素边界时,阻止继续滚动.
GitHub - willmcpo/body-scroll-lock: Body scroll locking that just works with everything 😏

See the Pen vMYyQg by aaronbird (@aaronbird) on CodePen.

原理

监控可滚动元素的touchmove事件, 判断手指的滑动方向和元素的滚动距离.
如果元素滚动到顶时用户继续下拉,则将scrollTop重置为零,同时调用event.preventDefault阻止页面滚动/刷新等默认动作.
如果元素滚动到底时用户继续上滑, 则将scrollTop重置为最大可滚动距离, 同时调用preventDefault.

注意: 如果将tochmove事件挂在window document document.body这三个元素上,则必需设置addEventListener的第三个参数的passive属性为false.否则浏览器会忽略事件函数中调用的preventDefault.

代码

const fixScroll = (scrollEl) => {
  let startY
  scrollEl.addEventListener('touchstart', function (event) {
    // 如果多于1根手指点击屏幕,则不处理
    if (event.targetTouches.length > 1) {
      return
    }
    // 储存手指的初始位置
    startY = event.targetTouches[0].clientY
  }, false)
  scrollEl.addEventListener('touchmove', function (event) {
    if (event.targetTouches.length > 1) {
      return
    }
    // 判断手指滑动方向, y大于0时向下滑动, 小于0时向上滑动
    const y = event.targetTouches[0].clientY - startY
    // 如果到顶时继续向下拉
    if (scrollEl.scrollTop <= 0 && y > 0) {
      // 重置滚动距离为最小值
      scrollEl.scrollTop = 0
      // 阻止滚动
      event.preventDefault()
    }
    // 如果到底时继续上滑  
    const maxScrollTop = scrollEl.scrollHeight - scrollEl.clientHeight
    if (maxScrollTop - scrollEl.scrollTop <= 0 && y < 0) {
      scrollEl.scrollTop = maxScrollTop
      event.preventDefault()
    }
  }, {
    passive: false
  })
}

See the Pen fix scroll 例子 by aaronbird (@aaronbird) on CodePen.

定位元素下的滚动穿透

如果定位元素下有滚动元素,也会发生滚动穿透现象.
1.gif
如上图所示, 半透明的上层元素是一个fixed元素.即使在触发touchmove事件时调用event.stopPropagation,也不能阻止滚动穿透.

另外要注意,即便用了fixScroll函数, 也必须在touchmove事件中调用event.stopPropagation.否者滑到底时会卡住.
1.gif

原因是touchmove事件穿透到下层去了
2.PNG

参考

javascript - iOS 10 Safari: Prevent scrolling behind a fixed overlay and maintain scroll position - Stack Overflow
在 iOS 下使用 iframe 的种种问题 - xiaOp的博客