概述
事件委托也叫事件代理,就是把目标节点的事件绑定到祖先节点上。基于事件传播过程,逐层冒泡总能被祖先节点捕获。可以只指定一个事件处理程序,就可以管理某一类型的所有事件。这样的好处是省监听数,可以监听动态元素。
举个例子,公司里有个三个同事预计在同一天收到快递。为签收快递,有两种方法:一、三个人在公司门口等快递;二、委托给前台代为签收。
现实生活中我们一般采用委托的方案。前台收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台也会在收到寄给新员工的快递后核实并代为签收。
这里其实有两层意思:
第二,新员工也是可以被前台代为签收的,即程序中新添加的dom节点也是有事件的。
使用场景
- 场景一 很多的dom需要添加事件处理,比如100个按钮添加点击事件,每个按钮都有相同的点击事件,然而这样就需要遍历所有的按钮,来给他们给他们添加事件。
这样就会延长整个页面交互就绪时间,我们只需要监听这100个按钮的祖先,等冒泡的时候判断target是不是这个100个按钮中的一个。
//div为100个button的祖先元素
div.addEventLister('click',(e)=>{
const t = e.target
if(t.tagName.toLowerCase()==='button'){
console.log('button data-id 是'+ t.dataset.id);
console.log('button 内容是'+ t.textContent);
}
})
- 场景二 要监听不存在的元素点击事件,即还没有元素还没有被添加进去。
//div里延时创建一个button
setTimeout(() => {
const button = document.createElement('button')
button.textContent = 'click'
div.appendChild(button)
}, 1000);
div.addEventListener('click',(e)=>{
const t = e.target
if(t.tagName.toLowerCase()==='button'){
console.log('button :'+t.textContent);
}
})
原理
事件委托利用了冒泡原理来实现,即事件从最深的节点开始,然后逐步向上传播事件。
封装事件委托
有一个自己定义的on函数,当用户点击#div里的button时调用。
on('click','#div','button',()=>{
console.log('button被点击');
})
function on(eventType,element,selector,fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}//判断是不是个元素,不是则去找这个元素
element.addEventListener(eventType,(e)=>{
const t = e.target
if(t.matches(selector)){
fn(e)
}
})
}
JS不支持事件,JS只是调用DOM提供的addEventListener。
其他内容
target
和currentTarget
的区别:
e.target
——用户操作的元素e.currentTarget
——程序员监听的元素
手写一个可以拖动的div
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="xxx"></div>
</body>
<style>
div{
border: 1px solid red;
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
*{margin:0; padding: 0;}
</style>
<script>
var dragging = false
var position = null
xxx.addEventListener('mousedown',function(e){
dragging = true
position = [e.clientX, e.clientY]
})
document.addEventListener('mousemove', function(e){
if(dragging === false){return}
console.log('hi')
const x = e.clientX
const y = e.clientY
const deltaX = x - position[0]
const deltaY = y - position[1]
const left = parseInt(xxx.style.left || 0)
const top = parseInt(xxx.style.top || 0)
xxx.style.left = left + deltaX + 'px'
xxx.style.top = top + deltaY + 'px'
position = [x, y]
})
document.addEventListener('mouseup', function(e){
dragging = false
})
</script>
</html>