事件委託:利用事件冒泡的原理,讓自己的所觸發的事件,讓他的父元素代替執行!支持爲同一個DOM元素註冊多個同類型事件。
說到事件冒泡,那麼就要說一下事件捕獲了。
-
事件捕獲 當一個事件觸發後,從Window對象觸發,不斷經過下級節點,直到目標節點。在事件到達目標節點之前的過程就是捕獲階段。所有經過的節點,都會觸發對應的事件
-
事件冒泡 當事件到達目標節點後,會沿着捕獲階段的路線原路返回。同樣,所有經過的節點,都會觸發對應的事件
當用戶觸發一個事件時,瀏覽器的所有詳細信息都存在一個叫event的對象上。我們把他叫事件對象,所有事件在綁定方法的時候,天生自帶一個參數就叫event。
document.οnclick=function(e){
var e=e||window.event
}
正常情況下,如果不加限制,事件都是進行事件冒泡機制的.
如下,先打印div1,後打印body。
<body>
<div id="div1">11</div>
</body>
<script>
window.onload = function(){
let body = document.querySelector('body');
let div1 = document.getElementById('div1');
body.onclick = function(){
console.log('打印body')
}
div1.onclick = function(){
console.log('打印div1')
}
}
//打印div1
// 打印body
</script>
要是想要進行事件捕獲怎麼辦呢?
<div id="div1">11</div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.onclick = function(){
console.log('打印第一次')
}
div1.onclick = function(){
console.log('打印第二次')
}
}
//只打印第二次
-
標準瀏覽器用addEventListener(type,listener,useCapture)實現
- type: 必須,String類型,事件類型
- listener: 必須,函數體或者JS方法
-
useCapture: 可選,boolean類型。指定事件是否發生在捕獲階段。默認爲false,事件發生在冒泡階段
- 以往註冊事件的方法,如果存在多個事件,後註冊的事件會覆蓋先註冊的事件
<div id="div1">11</div>
window.onload = function(){
let div1 = document.getElementById('div1');
div1.addEventListener('click',function(){
console.log('打印第一次')
})
div1.addEventListener('click',function(){
console.log('打印第二次')
})
}
//打印第一次
//打印第二次
這樣後面的就不會覆蓋前面的了。
如下所示:把useCapture的值設爲true,就會進行事件捕獲了。這樣body就會先輸出。
<body>
<div id="div1"></div>
</body>
<script>
window.onload = function(){
let body = document.querySelector('body');
let div1 = document.getElementById('div1');
body.addEventListener('click',function(){
console.log('打印body')
},true)
div1.addEventListener('click',function(){
console.log('打印div1')
})
}
//打印body
//打印div1
</script>
那什麼樣的事件可以用事件委託,什麼樣的事件不可以用呢?
- 適合用事件委託的事件:click,mousedown,mouseup,keydown,keyup,keypress。
- 值得注意的是,mouseover 和 mouseout 雖然也有事件冒泡,但是處理它們的時候需要特別的注意,因爲需要經常計算它們的位置,處理起來不太容易。
- 不適合的就有很多了,舉個例子,mousemove,每次都要計算它的位置,非常不好把控,在不如說 focus,blur 之類的,本身就沒用冒泡的特性,自然就不用事件委託了。
事件委託的優點:
- 提高性能:每一個函數都會佔用內存空間,只需添加一個事件處理程序代理所有事件,所佔用的內存空間更少。
- 動態監聽:使用事件委託可以自動綁定動態添加的元素,即新增的節點不需要主動添加也可以一樣具有和其他元素一樣的事件。 例子解析:
target 事件屬性可返回事件的目標節點(觸發該事件的節點),如生成事件的元素、文檔或窗口。
<ul>
<li>11</li>
<li>22</li>
<li>33</li>
</ul>
// good
document.querySelector('ul').onclick = (event) => {
let target = event.target
if (target.nodeName === 'LI') {
console.log(target.innerHTML)
}
}
// bad
document.querySelectorAll('li').forEach((e) => {
e.onclick = function() {
console.log(this.innerHTML)
}
})
就這樣父級綁定了點擊事件,點擊子元素後冒泡上去的時候,執行父級的事件。
事件委託怎麼取索引?
<ul id="ul">
<li>aaaaaaaa</li>
<li>事件委託了 點擊當前,如何獲取 這個點擊的下標</li>
<li>cccccccc</li>
</ul>
<script>
window.onload = function () {
var oUl = document.getElementById("ul");
var aLi = oUl.getElementsByTagName("li");
oUl.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == "li") {
var that = target;
var index;
for (var i = 0; i < aLi.length; i++)
if (aLi[i] === target) index = i;
if (index >= 0) alert('我的下標是第' + index + '個');
target.style.background = "red";
}
}
}
</script>