移動端在touch上一共有4個事件
touchstart touchmove touchend touchcancel, touchcancel, 一般來說,它們執(zhí)行的順序為 touchstart -> touchmove -> touchend -> touchcancel . 其中touchcancel一般情況下不會觸發(fā)剔应,也不是這里討論的焦點。
這里會結(jié)合click對上面的事件進行討論, touch發(fā)生在click之前
先上段代碼,直觀感受一下
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#level0 {
/* width: 500px;
height: 500px; */
}
#level1-0 {
background: red;
width: 500px;
height: 500px;
}
#level1-1 {
background: green;
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div id="level0">
<div id="level1-0">
</div>
<div id="level1-1">
</div>
</div>
</body>
<script type="text/javascript">
var level10 = document.getElementById("level1-0");
level10.addEventListener('touchstart', function(e) {
console.log(1);
});
level10.addEventListener('touchmove', function(e) {
console.log(2);
});
level10.addEventListener('touchend', function(e) {
console.log(3);
});
level10.onclick = function() {
console.log(5);
}
document.body.onclick = function() {
console.log('6');
}
</script>
</html>
在紅色區(qū)域點擊會出現(xiàn)什么效果呢造虏? 出現(xiàn)的是 1 3 5 6, 奇怪了 touchmove 為何不執(zhí)行麦箍,因為我們并沒有移動漓藕,也就是說,必須觸碰到屏幕上面挟裂,而且發(fā)生了移動動作享钞,touchmove才執(zhí)行,現(xiàn)在我們觸碰到,而且手指稍微動一下栗竖,發(fā)現(xiàn)輸出的效果是暑脆, 1 2(+) 3, 其中touchmove 可能觸發(fā)多次,又奇怪了狐肢, click為何不執(zhí)行添吗, 因為 click執(zhí)行的條件是 點擊, 而且不移動 所以一般情況下份名,我們可以理解成 touchmove和click是相斥的碟联。
我們知道,當一個用戶在點擊屏幕的時候僵腺,系統(tǒng)會觸發(fā)touch事件和click事件鲤孵,touch事件優(yōu)先處理,touch事件經(jīng)過 捕獲辰如,處理, 冒泡 一系列流程處理完成后普监, 才回去觸發(fā)click事件
既然touch事件和click事件有了優(yōu)先級別,那么能不能在touch階段取消掉系統(tǒng)觸發(fā)的click事件呢琉兜?當然是可以的鹰椒,瀏覽器提供了這樣的能力。在touch事件里面呕童,調(diào)用e.preventDefault() 就可以阻止本次點擊系統(tǒng)觸發(fā)的click事件漆际,即本次相關(guān)的click都不會執(zhí)行
把上面代碼稍微加一點
level10.addEventListener('touchstart', function(e) {
console.log(1);
e.preventDefault();
});
點擊的時候 發(fā)現(xiàn) 只有 1 3, 說明click被阻止了夺饲,當然在touchend里面加效果也一樣奸汇,所以 在touch事件里面加 e.preventDefault可以取消系統(tǒng)產(chǎn)生的click事件, 當然不會阻止后面的touch事件往声。
用個具體的例子看看 如何解決點透問題
產(chǎn)生點透問題的原因擂找, 可以先看看代碼吧
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#level0 {
/* width: 500px;
height: 500px; */
position: relative;
}
#level1-0 {
position: absolute;
z-index: 1;
background: red;
width: 500px;
height: 500px;
}
#level1-1 {
background: green;
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<div id="level0">
<div id="level1-0">
</div>
<div id="level1-1">
</div>
</div>
</body>
<script type="text/javascript">
var level10 = document.getElementById("level1-0");
var level11 = document.getElementById("level1-1");
level10.addEventListener('touchstart', function(e) {
level10.style.display = 'none';
});
level11.onclick = function() {
console.log('level11莫名被點擊了');
}
</script>
</html>
本來是 level1-0 和 level1-1是兄弟節(jié)點,即他們之間不會發(fā)生什么 事件傳遞浩销, 目前l(fā)evel1-0相當于一個覆蓋層贯涎,覆蓋在level1-1上面, 按理說點擊 level1-0的時候慢洋,level1-0會阻擋所有的事件塘雳,事件不會傳遞給level1-1,當點擊level1-0的時候普筹,實際上level1-1也發(fā)生了點擊事件败明,即上面的輸出結(jié)果為
level1-0消失, 輸出 level11莫名被點擊了
, 這就是點透
點透發(fā)生的條件太防,
- A 和 B不是后代繼承關(guān)系(如果是后代繼承關(guān)系的話妻顶,就直接是冒泡子類的話題了)
- A發(fā)生touch, A touch后立即消失, B事件綁定click
- A z-index大于B讳嘱,即A顯示在B浮層之上
點透發(fā)生的理由: 當手指觸摸到屏幕的時候幔嗦,系統(tǒng)生成兩個事件,一個是touch 一個是click沥潭,touch先執(zhí)行崭添,touch執(zhí)行完成后,A從文檔樹上面消失了叛氨,而且由于移動端click還有延遲200-300ms的關(guān)系呼渣,當系統(tǒng)要觸發(fā)click的時候,發(fā)現(xiàn)在用戶點擊的位置上面寞埠,目前離用戶最近的元素是B屁置,所以就直接把click事件作用在B元素上面了
.
那如何才能解決點透問題呢? 還記得我之前說過么仁连,系統(tǒng)提供了先觸發(fā)的touch事件去取消系統(tǒng)生成的click事件蓝角,所以只要在touch事件的某個處理函數(shù)中 執(zhí)行 e.preverDefault即可, 一般我們在touchend中執(zhí)行
在上面代碼中饭冬,加上這句就完美解決了
level10.addEventListener('touchend', function(e) {
e.preventDefault();
});
當然點透問題使鹅,還有其他的解決方法,關(guān)鍵是 要么是需求本次系統(tǒng)生成的click事件昌抠,要么是當系統(tǒng)觸發(fā)click的時候患朱,當前的觸發(fā)touch的那個dom節(jié)點還存在。比如將其一延遲3s在關(guān)閉
setTimeout(() => {
level10.style.display = 'none';
}, 300);