移動端的點擊事件與Sticky Hover問題
v1.0
作者:ZBW
TL;DR
解決辦法是:在:hover偽類外使用@media區分設備,在移動設備上使hover效果不生效。
@media (hover: hover) {
.desexp-text:hover {
opacity:1;
}
}
這一方法的缺點是問題在帶有觸控的PC上仍可能存在,但對於大多移動設備來說足以應付了。
前言
筆者起初認為該問題是JS的點擊事件綁定有誤導致的,但之后卻發現問題出在CSS上。因此本文除了解決問題外,也會介紹以下筆者的踩坑歷程,順帶介紹一下移動端點擊事件的不同。
問題描述
背景
在Beta階段,團隊想要在項目中實現設計性實驗復習的頁面。該頁面的主要使用方式是團隊預先在整理好的復習資料中挖一些空,然后用戶通過交互來顯示/隱藏答案。計划的交互方式是:
-
鼠標放在空上,答案顯示出來。鼠標移開答案消失
-
鼠標點一下空,答案顯示,移開鼠標也不會消失。再次點擊將恢復隱藏的狀態。
-
實現一個清空答案的按鈕,可以一鍵清空所有顯示的答案。
實現方式
該功能的實現方式非常簡單。對於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上原先的問題仍可能存在,但對於解決移動端問題來說該方法非常實用。