【原】移動端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只貓,祝你們聖誕快樂~ 喵

相關文章
相關標籤/搜索