- 事件參數、事件捕獲冒泡
- 事件委託
- 事件默認行爲
- DOM 1 、DOM 2、DOM 3模型
- 實現遮罩功能(點擊穿透、移動端點擊事件300s延遲問題)
注意有些demo需要在開發者工具中切換PC端,移動端模擬器
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<title>事件</title>
<style>
* {
margin: 0;
padding: 0;
}
.div1 {
width: 300px;
height: 300px;
border: 1px green solid;
}
.div1 .div2 {
width: 200px;
height: 200px;
border: 1px green solid;
}
.div1 .div2 .div3 {
width: 100px;
height: 100px;
border: 1px green solid;
}
.container {
width: 300px;
height: 200px;
border: 1px black solid;
position: relative;
}
.container .under-layer {
width: 100%;
height: 100%;
background-color: pink;
}
.container .input {
position: absolute;
top: 50%;
left: 50%;
width: 100px;
height: 30px;
margin-top: 20px;
margin-left: -100px;
}
.container .popup {
background-color: #eeeeee;
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -50px;
margin-left: -50px;
z-index: 1;
display: none;
}
.container .popup .layer-title {
text-align: center;
margin-top: 20px;
}
.container .popup .layer-action {
text-align: center;
margin-top: 20px;
}
.container .popup .layer-action .btn {
background-color: #0A98D5;
color: white;
width: 60px;
height: 30px;
}
.bg-mask {
position: fixed;
width: 100%;
height: 100%;
top: 0;
background-color: rgba(0, 0, 0, 0.5);
/*pointer-events: none;*/
display: none;
}
.entrust {
border: 1px green solid;
}
.entrust li {
border: 1px #0A98D5 solid;
height: 50px;
}
.no-select {
user-select: none;
}
</style>
</head>
<body>
<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Event">事件參數詳解</a>
<div class="div1" id="div1">
div1
<div class="div2" id="div2">
div2
<div class="div3" id="div3">
div3
</div>
</div>
</div>
<button id="showMask">顯示遮罩</button>
<div class="container">
<div class="under-layer" id="underLayer">遮罩功能 底層元素</div>
<input class="input" type="text">
<div class="popup" id="popupLayer">
<div class="layer-title">彈出層</div>
<div class="layer-action">
<button class="btn" id="closePopup">關閉</button>
</div>
</div>
</div>
<div class="bg-mask" id="bgMask"></div>
<ul id="entrust" class="entrust">
<li>1</li>
<li>2</li>
<li id="li-3">3</li>
</ul>
<p class="no-select">通過css user-select: none; 設置無法被選中</p>
<p class="select">可以被選中</p>
<p>禁止圖片的默認行爲</p>
<img src="https://github.wdapp.top/github/res/images/icon/CC_144_144.png" alt="" id="img">
<ul>
<li><a href="#">禁止a便籤默認行爲 href="#"</a></li>
<li><a href="javascript:void(0)">禁止a便籤默認行爲 href="javascript:void(0)"</a></li>
<li><a href="javascript:;">禁止a便籤默認行爲 href="javascript:;"</a></li>
<li><a href="#" onclick="onClick()">禁止a便籤默認行爲 οnclick="onClick()"</a></li>
<li><a href="#" onclick="handleClick();return false;">禁止a便籤默認行爲 onclick="handleClick();return false;"</a></li>
<li><a href="http://www.baidu.com" onclick="return false;">禁止a便籤默認行爲 onclick="return false;"</a></li>
<li><a href="https://www.baidu.com" id="link">禁止a便籤默認行爲 event.preventDefault()</a></li>
</ul>
<script>
// 1.事件參數、事件捕獲冒泡
// 2.事件委託
// 3.事件默認行爲
// 4.DOM 1 、DOM 2、DOM 3模型
// 5.實現遮罩功能
// 1.事件參數、事件捕獲冒泡
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
var div3 = document.getElementById("div3");
var img = document.getElementById("img");
var link = document.getElementById("link");
// once: ture 事件只觸發一次,
// capture: false 事件冒泡(默認)
// capture: true 事件捕獲
div1.addEventListener("click", function(event) {
console.log("div1", event);
// DOM3級新增事件stopImmediatePropagation()方法來阻止事件捕獲,另外此方法還可以阻止事件冒泡
// event.stopImmediatePropagation();
// stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕獲,也可以阻止處於目標階段。
// event.stopPropagation();
// stopImmediatePropagation() 和 stopPropagation()的區別
// 後者只會阻止冒泡或者是捕獲。 但是前者除此之外還會阻止該元素的其他事件發生,但是後者就不會阻止其他事件的發生。 https://developer.mozilla.org/zh-CN/docs/Web/API/Event/stopImmediatePropagation
// for (var key in event) {
// console.log("for in ", key);
// }
}, {
capture: false,
once: false
});
// useCaptrue事件捕獲
div2.addEventListener("click", function(event) {
console.log("div2", event);
// 禁止事件捕獲
// event.stopImmediatePropagation();
// event.stopPropagation();
}, false);
// 默認事件冒泡
div3.addEventListener("click", function(event) {
// 禁止事件冒泡
// event.stopPropagation();
// event.cancelBubble = true
// event.preventDefault()
console.log("div3", event);
}, false);
div1.addEventListener("dblclick", function(e) {
console.log("dblclick", e);
});
// 2.事件委託
var entrust = document.getElementById("entrust");
var li3 = document.getElementById("li-3");
entrust.addEventListener("click", function(e) {
console.log("entrust", e);
});
li3.addEventListener("click", function(e) {
console.log("li3", e);
});
// 3. 事件默認行爲
// 禁止圖片默認行爲,並且通過設置 passive: true 禁止設置event.preventDefault()
img.addEventListener("mousedown", function(event) {
event.preventDefault();
}, {
passive: true,
mozSystemGroup: true
});
// 禁止a便籤默認行爲
link.onclick = function(e) {
if (e && e.preventDefault) {
e.preventDefault();
} else {
window.event.returnValue = false;
}
};
function handleClick() {
console.log("handleClick");
}
function onClick() {
console.log("onClick");
return false;
}
// 4.事件模型與DOM 1 、DOM 2、DOM 3模型
// DOM0:不是W3C規範。
// DOM1:開始是W3C規範。專注於HTML文檔和XML文檔。
// DOM2:對DOM1增加了樣式表對象模型
// DOM3:對DOM2增加了內容模型 (DTD 、Schemas) 和文檔驗證。
// 內聯事件模型
// html
// <button onclick="callback()">btn</button>
// javascript
// function callback() {
// console.log("callback");
// }
// 腳本事件模型
// xhr.onprogress = function() { ... }
// DOM 2 事件綁定
// target.addEventListener(type, listener, options);
// target.addEventListener(type, listener, useCapture);
/*
* type 表示監聽事件類型的字符串。
* listener 當所監聽的事件類型觸發時,會接收到一個事件通知(實現了 Event 接口的對象)對象。listener 必須是一個實現了 EventListener 接口的對象,或者是一個函數。
* */
/*
* options 可選
* capture: Boolean,表示 listener 會在該類型的事件捕獲階段傳播到該 EventTarget 時觸發。
* once: Boolean,表示 listener 在添加之後最多隻調用一次。如果是 true, listener 會在其被調用之後自動移除。
* passive: Boolean,設置爲true時,表示 listener 永遠不會調用 preventDefault()。如果 listener 仍然調用了這個函數,客戶端將會忽略它並拋出一個控制檯警告。
* */
/*
* useCapture 可選
* Boolean,在DOM樹中,註冊了listener的元素, 是否要先於它下面的EventTarget,調用該listener。 當useCapture(設爲true) 時,沿着DOM樹向上冒泡的事件,不會觸發listener。當一個元素嵌套了另一個元素,並且兩個元素都對同一事件註冊了一個處理函數時,所發生的事件冒泡和事件捕獲是兩種不同的事件傳播方式。事件傳播模式決定了元素以哪個順序接收事件。進一步的解釋可以查看 事件流 及 JavaScript Event order 文檔。 如果沒有指定, useCapture 默認爲 false 。
* */
// 5.實現遮罩功能
// 需求:點擊按鈕開啓遮罩。點擊遮罩時,可以關閉遮罩,並且時間可以穿透到下一層,解決移動端300s延遲問題。
var showMask = document.getElementById("showMask");
var closePopup = document.getElementById("closePopup");
var popupLayer = document.getElementById("popupLayer");
var bgMask = document.getElementById("bgMask");
var underLayer = document.getElementById("underLayer");
var isBgMask = true;
showMask.addEventListener("click", function() {
popupLayer.style.display = "block";
bgMask.style.display = "block";
console.log("showMask");
isBgMask = true;
});
// PC端點擊過程可以拆解爲 mousedown -> mouseup -> click
// 移動端點擊過程可以拆解爲 touchstart -> touchend -> 300s -> click
// 移動端 click 存在300s延遲,爲了等待確認用戶行爲是點擊還是雙擊。touchstart、touchend、touchmove無延遲,click有延遲,如果touch事件後target元素消失,事件會穿透
// 當觸發 touchstart 之後等待 300s 如果隱藏 popupLayer 和 bgMask,click target 則會傳遞到 underLayer 上,觸發 click
var startTime = 0;
var endTime = 0;
var timeout = 0;
bgMask.addEventListener("touchstart", function(e) {
console.log("bgMask touchend");
e.preventDefault();//禁止觸發click事件,防止點擊事件穿透
startTime = performance.now();
closeMask();
});
// bgMask.addEventListener("click", function(e) {
// console.log("bgMask click", e);
// });
underLayer.addEventListener("click", function(e) {
endTime = performance.now();
timeout = endTime - startTime;
console.log("underLayer", ",endTime:" + endTime, ",startTime:" + startTime, ",timeout:" + timeout + "ms");
// alert("underLayer" + ",endTime:" + endTime + ",startTime:" + startTime + ",timeout:" + timeout + "ms");
});
function closeMask() {
popupLayer.style.display = "none";
bgMask.style.display = "none";
isBgMask = false;
}
// 解決移動端點擊事件300s延遲問題
// 1、禁用縮放 (chrome 和 firefox)
// 在 chrome 和 firefox 的移動版本中,如果禁用了頁面縮放,那麼着 300ms 的延遲就會自動消失,具體代碼如下:
// <meta name="viewport" content="width=device-width, user-scalable=no"> 或 <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
// 缺點:safari 上並不起作用、click事件觸發還是慢
// 2、使用指針事件 (IE10+)CSS 觸摸 action 屬性,它允許你移除特定元素或整個文檔的觸發延遲,而無需禁用縮放:
// a, button, .myelements {
// -ms-touch-action: manipulation; /* IE10 */
// touch-action: manipulation; /* IE11+ */
// }
// 3、使用 touchend 事件處理 不同於 click 和 touchstart。touchend 沒有這個 300ms 的延遲,而是即時觸發,你完全可以在 WebGL 或 canvase 遊戲中進行實踐,但是在web 頁面中單單使用 touchend 並不一定靠譜.
// 1、如果用戶在兩個不同元素之間觸發了 touchstart 和touchend,那麼 click 不會被觸發 .
// 2、如果用戶觸發了 touchstart,但是在touchend之前是一個長長的 touchmove 滾動, 那麼 click 也不會被觸發.
// 總結:可以使用touchend加event.preventDefault(),缺點是存在兼容性問題
// 4、使用fastclick.js,使用fastclick實現原理:
// var targetElement = null;
// document.body.addEventListener("touchstart", function() {
// console.log("document.body.touchstart")
// // 記錄點擊的元素
// targetElement = event.target;
// });
// document.body.addEventListener("touchend", function(event) {
// console.log("fastclick");
// // 阻止默認事件(屏蔽之後的click事件)
// event.preventDefault();
// var touch = event.changedTouches[0];
// // 合成click事件,並添加可跟蹤屬性forwardedTouchEvent
// var clickEvent = document.createEvent("MouseEvents");
// clickEvent.initMouseEvent("click", true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
// clickEvent.forwardedTouchEvent = true; // 自定義的
// targetElement.dispatchEvent(clickEvent);
// });
// 5. CSS3 的方法 雖然主要講的是事件,但是有必要介紹一個 CSS3 的屬性 —— pointer-events。
// pointer-events: auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit;
// auto 默認值,鼠標或觸屏事件不會穿透當前層
// none 元素不再是target,監聽的元素變成了下層的元素(如果子元素設置成 auto,點擊子元素會繼續監聽事件)
// bgMask.style.pointerEvents = "none" // PC事件穿透
// bgMask.addEventListener("mousedown", function(e) {
// console.log("bgMask mousedown");
// isBgMask = true
// });
// bgMask.addEventListener("mouseup", function(e) {
// console.log("bgMask mouseup");
// startTime = performance.now();
// closeMask();
// });
//
// document.body.addEventListener("click", function(e) {
// if (isBgMask) {
// popupLayer.style.display = "none";
// bgMask.style.display = "none";
// }
// },true);
// 點事事件在PC端觸發順序是 mousedown -> mousemove -> mouseup -> click
// document.body.addEventListener("mousedown", function(e) {
// console.log("mousedown", e)
// },true);
// document.body.addEventListener("mousemove", function(e) {
// console.log("mousemove", e)
// },true);
// document.body.addEventListener("mouseup", function(e) {
// console.log("mouseup",e)
// },true);
// document.body.addEventListener("click", function(e) {
// console.log("click", e)
// },true);
// 點擊事件在移動端的觸發順序 touchstart -> touchmove -> touchend -> click
// document.body.addEventListener("touchstart", function(e) {
// console.log("touchstart", e)
// },true);
// document.body.addEventListener("touchmove", function(e) {
// console.log("touchmove", e)
// },true);
// document.body.addEventListener("touchend", function(e) {
// console.log("touchend",e)
// },true);
// document.body.addEventListener("click", function(e) {
// console.log("click", e)
// },true);
</script>
</body>
</html>