【技術博客】移動端的點擊事件與Sticky Hover問題


移動端的點擊事件與Sticky Hover問題

v1.0

作者:ZBW

TL;DR

解決辦法是:在:hover偽類外使用@media區分設備,在移動設備上使hover效果不生效。

@media (hover: hover) {
    .desexp-text:hover  {
        opacity:1;
    }
}

這一方法的缺點是問題在帶有觸控的PC上仍可能存在,但對於大多移動設備來說足以應付了。

前言

筆者起初認為該問題是JS的點擊事件綁定有誤導致的,但之后卻發現問題出在CSS上。因此本文除了解決問題外,也會介紹以下筆者的踩坑歷程,順帶介紹一下移動端點擊事件的不同。

問題描述

背景

在Beta階段,團隊想要在項目中實現設計性實驗復習的頁面。該頁面的主要使用方式是團隊預先在整理好的復習資料中挖一些空,然后用戶通過交互來顯示/隱藏答案。計划的交互方式是:

  1. 鼠標放在空上,答案顯示出來。鼠標移開答案消失

  2. 鼠標點一下空,答案顯示,移開鼠標也不會消失。再次點擊將恢復隱藏的狀態。

  3. 實現一個清空答案的按鈕,可以一鍵清空所有顯示的答案。

實現方式

該功能的實現方式非常簡單。對於1,直接使用CSS的:hover偽類配合透明度即可解決。對於2和3,可以使用JS來監聽點擊事件,並且根據點擊事件和目前的顯示狀態對透明度進行切換。

為了實現這一功能,首先我們對所有挖空的文本外套了兩個html標簽(span和text),並分別以class區分,形如:

<span class='desexp-span'><text class='desexp-text'>被挖空的內容</text></span>

並且分別以如下的CSS和JS實現上述的效果

span.desexp-span {
    border-bottom: 1px solid rgb(200, 200, 200);
}
.desexp-text {
    opacity:0;
    transition: opacity 1s;
    -webkit-transition: opacity 1s;
}
.desexp-text:hover  {
    opacity:1;
}
var inners = document.getElementsByClassName("desexp-text");
var myfunction = function () {
    if (this.style.opacity === 1)
        this.style.opacity = "";
    else
        this.style.opacity = 1;
};
for (var i = 0; i < inners.length; i++) {
    inners[i].addEventListener('click', myfunction, false);
}

清空答案的按鈕實現也很簡單(代碼中是從iframe內讀取內容):

    let inners = document.getElementById("desexp-iframe").contentWindow.document.getElementsByClassName("desexp-text");
    for (var i = 0; i < inners.length; i++) {
        inners[i].style.opacity = "";
    }

實現的效果如下:

問題

該實現在PC端的瀏覽器下表現正常,但當使用手機進行操作時就會出現問題。

在手機上點擊挖空的位置,答案顯示正常。當再次點擊挖空時,答案卻不會消失,而如果此時點擊另一個空,之前的答案便會消失。除此之外清空答案按鈕不起作用。

關於移動端瀏覽器的點擊事件

自第一代iPhone於2007年發布以來,人們在手機上瀏覽網頁的方式發生了很大的變化。2007年時大多數網頁並沒有考慮過在手機上被瀏覽的問題,為了在iPhone上方便瀏覽桌面網頁,工程師們實現了雙擊放大的功能,使在手機瀏覽器上雙擊屏幕就能將網頁內容放大。從而為了區分雙擊放大和點擊操作,瀏覽器在監聽到點擊事件后,會等待300ms判斷用戶是否再次點擊屏幕,只有300ms后才會觸發正常的點擊事件。

初次發現問題后各種解決嘗試:從點擊事件本身下手

cursor: pointer

該方法來源於此:stackoverflow

看起來iOS Safari中的點擊事件是一個非常麻煩的問題,回答提出的解決辦法非常簡單,在CSS中開頭添加:

cursor: pointer

區分設備分別使用click和touchend

來源:如何解決移動端的點擊事件?

var clickEvent = 'ontouchend' in document ? 'touchend' : 'click';
$(ele).on(clickEvent, function(event) {
    // 如果在移動端,一定要記得阻止默認事件
    event.preventDefault();
    // do something
})

以上的方法並不湊效。

問題的根源:CSS中:hover偽類在移動端的表現問題

實際上在筆者嘗試了很多辦法之后,意識到了問題的所在其實不在點擊事件上,因為點擊顯示的效果是正常的,300ms的影響也不大,點擊事件在設備上能夠被正常觸發。

使用Chrome的響應式界面調試時發現,第二次觸摸后標簽上的style屬性被正常移除了,說明JS代碼工作正常。因而讓文字透明度保持為1的也只有:hover屬性了。

經查資料后發現,:hover在移動端的表現類似於PC端的:focus,由於沒有鼠標指針的存在,點擊元素時:hover就會生效,並且直到點擊別的地方時:hover的作用才會消失。

除此之外,iOS Safari在以上問題上還存在bug,點擊別的地方時:hover也不會消失,被稱為Sticky Hover問題:iOS 'Sticky Hover' Fix。只有點擊另一個可以被focus的元素時之前的hover才會失效

解決辦法

來源:How to prevent sticky hover effects for buttons on touch devices

該方法應用了CSS中的媒體查詢(media query),檢查瀏覽器是否支持hover功能。支持hover功能的設備往往具有獨立的鼠標指針輸入(不限於PC,也可能是某些游戲機內的瀏覽器,使用搖桿操作鼠標指針)。

在CSS中應用方法如下:

@media (hover: hover) {
    .desexp-text:hover  {
        opacity:1;
    }
}

簡而言之,即用@media將:hover偽類的內容括起來即可。從而在不能使用鼠標指針的設備上就不存在該效果了。

該方法的一些小問題是在一些支持觸控的PC上原先的問題仍可能存在,但對於解決移動端問題來說該方法非常實用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM