问题描述:
在ios端,当页面可以滚动,并且弹出的弹窗也可以滚动时,在弹窗区域滚动时底部的被遮盖区域也在滚动。

问题原因:
滚动穿透。

解决思路:
弹窗显示时禁止body的滚动事件,弹窗关闭时恢复body的滚动事件。

test.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods:{
    handler: function(e) {
      e.preventDefault();
    },
    closeTouch() {
      document
        .getElementsByTagName("body")[0]
        .addEventListener("touchmove", this.handler, { passive: false }); //阻止默认事件
    },
    openTouch() {
      document
        .getElementsByTagName("body")[0]
        .removeEventListener("touchmove", this.handler, { passive: false }); //打开默认事件
    }
}

在显示弹窗的事件内调用closeTouch(),在隐藏弹窗的事件内调用openTouch()即可

若使用UI框架,如mint-ui时,关闭弹窗时没有回调事件,则监听弹窗的model即可

test.vue
1
2
3
4
5
6
7
8
9
watch: {
    visible: function(news, olds) {
      if (news) {
        this.closeTouch();
      } else {
        this.openTouch();
      }
    }
  }

另外,注意一下passive: false。为什么要加这句呢?

如果没有这句的话,控制台大概会有如下提示:

console.js
1
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

由于浏览器必须要在执行事件处理函数之后,才能知道有没有调用过preventDefault(),这就导致了浏览器不能及时响应滚动,略有延迟。

所以为了让页面滚动的效果如丝般顺滑,从 chrome56 开始,在windowdocumentbody 上注册的 touchstarttouchmove 事件处理函数,会默认为是 passive: true。浏览器忽略 preventDefault() 就可以第一时间滚动了。

举例: wnidow.addEventListener('touchmove', func) 效果和下面一句一样 wnidow.addEventListener('touchmove', func, { passive: true })

这就导致了一个问题:
如果在以上这 3 个元素的 touchstarttouchmove 事件处理函数中调用 e.preventDefault() ,会被浏览器忽略掉,并不会阻止默认行为。

因此,要加上passive: false,明确声明为不是被动的。

另外,还有一种方法:
应用 CSS 属性 touch-action: none; 这样任何触摸事件都不会产生默认行为,但是 touch 事件照样触发。关于touch-action