【原】移动端vue页面点透事件 - 分析与解决

近期项目遇到了vue页面事件被带到下一个页面的问题,也就是咱们常说的点透事件,主要表如今android机器上,花了很多时间折腾,简单作下总结~javascript

 

  • vue页面之间的切换经过Vue Router的router.push方法
  • b.vue以前已经访问过,数据经过vuex管理,从a.vue进入到b.vue再也不请求数据,直接拿到b.vue数据展现页面;
  • a.vue页面上点击最底部的帐单后,不到100ms就打开b.vue页面,此时最底部的帐单的触摸事件并无消失,a.vue的触摸事件直接平移到b.vue最底部位置,恰好最底部有个按钮,致使直接打开c.vue。

验证是否由于300ms延迟致使的问题

早在2013作移动端页面时候就据说过点透事件,由click事件引发的300ms的延迟致使:css

移动设备上的web网页是有300ms延迟的,每每会形成按钮点击延迟,引发页面点透或是点击失效。html

2007年苹果发布首款iphone上IOS系统搭载的safari为了将适用于PC端上大屏幕的网页能比较好的展现在手机端上,使用了双击缩放(double tap to zoom)的方案,好比你在手机上用浏览器打开一个PC上的网页,你可能在看到页面内容虽然能够撑满整个屏幕,可是字体、图片都很小看不清,此时能够快速双击屏幕上的某一部分,你就能看清该部分放大后的内容,再次双击后能回到原始状态。vue

双击缩放是指用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。java

缘由就出在浏览器须要如何判断快速点击上,当用户在屏幕上单击某一个元素时候,例如跳转连接<a href="#"></a>,此处浏览器会先捕获该次单击,但浏览器不能决定用户是单纯要点击连接仍是要双击该部分区域进行缩放操做,因此,捕获第一次单击后,浏览器会先Hold一段时间t,若是在t时间区间里用户未进行下一次点击,则浏览器会作单击跳转连接的处理,若是t时间里用户进行了第二次单击操做,则浏览器会禁止跳转,转而进行对该部分区域页面的缩放操做。那么这个时间区间t有多少呢?android

在IOS safari下,大概为300毫秒。这就是延迟的由来。形成的后果用户纯粹单击页面,页面须要过一段时间才响应,给用户慢体验感受,对于web开发者来讲是,页面js捕获click事件的回调函数处理,须要300ms后才生效,也就间接致使影响其余业务逻辑的处理。ios

事隔7年,浏览器厂商还没修复这个问题么?楼主使用iPhone X、华为、vivo、小米等主流的手机来测试。web

首先咱们在移动端网页使用双击缩放模式(不设置viewport)vuex

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试</title>
<script type="text/javascript" class="library" src="https://cdn.bootcss.com/zepto/1.1.7/zepto.js"></script>
<style type="text/css">
    #btn{
        margin: 50px 0;
        width: 200px;
        height: 40px;
        text-align: center;
        font-size: 16px;
    }
</style>
</head>
<body>
    <button id="btn">点我查看事件响应时间</button>

<script type="text/javascript">
    let startTime;
    let log = function (msg) {
        let div = $('<div></div>');
        div.html((new Date().getTime()) + ': ' + (new Date().getTime() - startTime) + ': ' + msg)
        $('body').append(div);
    };
    let touchStart = function () {
        startTime = new Date().getTime();
        log('touchStart');
    };
    let touchEnd = function () {
        log('touchEnd');

    };

    let click = function () {
        log('click');
    };
    let mouseUp = function () {
        log('mouseUp');

    };
    let mouseDown = function () {
        log('mouseDown');
    };
    
    let btn = $('#btn');
    
    btn.bind('touchstart', touchStart);
    btn.bind('touchend', touchEnd);
    btn.bind('mousedown', mouseDown);
    btn.bind('mouseup', mouseUp);
    btn.bind('click', click);
</script>
</html>

对应日志以下,能够看到没有添加viewport的状况,ios能够看到click事件300ms的延迟问题仍是存在,Android表现正常。npm

接着,页面添加viewport:<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">的状况,ios和android触发事件后打印的日志并无300ms延迟。

从上面结果看出300ms的延迟主要表如今没有设置viewport的ios手机上,H5页面咱们通常会设置viewport:<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">

因此楼主认为咱们正常的H5页面中click事件是不存在300ms延迟的状况

 

那Android手机上点透事件到底怎么回事呢?

为了进一步验证不是300ms延迟引发的问题,采用互联网常见的防点透方案来解决问题:

比较流行的解决方案有v-tapvue2-touch-events自定义的tap事件,原理是把三个基础触摸事件:touchstart、touchmove、touchend组合起来叫tap事件,不使用click。

然而,通过反复测试后不能解决问题,因此楼主猜测本次点透并非300ms引发的。

 

还有一种解决点透的方案是添加透明遮罩层,一般在跳转的下一个页面上,有一个透明遮罩层置于最顶层,从上一个页面平移的事件不能触发当前页面的事件,而后过300ms后再隐藏该遮罩层,以此来阻止点透,能够暴力解决点透问题。

 

检查了此次项目中代码的HTML结构,其中包含touchstart、touchmove、touchend 、click 4个事件,测试发现click事件的代码并无起做用,

<template>
<div
    class="item"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend="touchEnd"
    @click="clickItem"
>
    <slot :data="item"></slot>
</div>
</template>

 由于touchend触发后有个路由的方法router.push让页面跳转了,click事件并无生效,也就是touchend后页面A把事件带到了页面B上,又恰好命中页面B上的按钮,从而致使页面C被打开的点透效果!

touchEnd: function (event) {
    this.$emit('goToDetail')//跳转detail页面
}

发现缘由:元素touchend以后触发浏览器默认行为致使点透

猜想给div标签绑定touchend事件后,元素有了浏览器默认的行为,具体的默认行为是什么呢,但它很是像click事件被带到B页面。为了验证猜想,在touchEnd方法中最后一行前添加 event.preventDefault() 

preventDefault是事件对象(Event)的一个方法,做用是取消浏览器事件的默认行为;

cancelable也是事件对象(Event)的一个方法, 代表该事件是否能够被取消默认行为,若是该事件能够用 preventDefault() 能够阻止与事件关联的默认行为,则返回 true,不然为 false

touchEnd: function (event) {
    this.$emit('goToDetail')
    if(event.cancelable) {
        event.preventDefault()
    }
}

解决方案:使用preventDefault()来阻止点透

测试发现 event.preventDefault() 能成功阻止了androids手机上vue页面切换致使事件点透的问题(目前ios并无发现事件点透问题,可能在多个版本前修复了这个体验),也验证了猜测:div标签绑定touchend事件后,元素有了浏览器默认的行为

vue页面开发,在HTML结构上添加事件修饰符.prevent,即@touchend.prevent一样能够调用 event.preventDefault()来阻止默认行为

<template>
<div
    class="item"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend.prevent="touchEnd"
>
    <slot :data="item"></slot>
</div>
</template>

使用click做为最终事件也能够防止点透

div标签绑定touchend事件后,元素有了浏览器默认的行为,这个所谓的默认行为又触发click事件,从而引发点透的问题。

那么,若是把this.$emit('goToDetail')的方法绑定到click上,是否能够解决点透问题?

<template>
<div
    class="item"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend="touchEnd"
    @click="click"
>
    <slot :data="item"></slot>
</div>
</template>
click: function () {
    this.$emit('goToDetail')
}

 通过测试,在click事件触发this.$emit('goToDetail')方法,页面跳转成功后并不会引发点透的问题。

 

总结:

  • Android设备中,div等标签绑定touchend事件后,元素有了浏览器默认的行为,好比触发click
  • 移动端vue页面点透事件可使用事件修饰符.prevent或event.preventDefault() 来阻止浏览器默认行为

 

最后晒上家里2只猫,祝你们圣诞快乐~ 喵

相关文章
相关标签/搜索