DOM 事件模型及DOM 事件机制
通俗理解,事件是用户或者浏览器自己执行的某种动作,是文档或者浏览器发生的一些交互瞬间,比如点击(click)按钮等,这里的click就是事件的名称。JS与html之间的交互是通过事件实现的,DOM支持大量的事件。
事件流
规定的事件流有三个阶段: 事件捕获阶段,目标阶段,事件冒泡阶段。
示例:
<div class="A">
<div class="B">
<div class="C">
Click
</div>
</div>
</div>
复制代码
捕获
捕获是从A -> B -> C;看有没有函数监听
由网景公司提出的:事件有父元素传递到子元素的过程就叫捕获
冒泡
冒泡是从C -> B -> A;看有没有函数监听
由微软提出的:事件由子元素传递到父元素传递的过程,叫做冒泡
总结
由外向内找监听函数叫事件捕获;
由内向外找监听函数叫事件冒泡。
W3C标准
W3C标准:首先捕获,再冒泡
绑定在C的事件是按照代码的顺序发生的,其他非C元素则是通过冒泡或者捕获的触发。按照W3C的标准,先发生捕获事件,后发生冒泡事件。
所以事件的整体顺序是:
A元素捕获 -> B元素捕获 -> C元素代码顺序 -> B元素冒泡 -> A元素冒泡
事件绑定
API addEventListener
W3C: baba.addEventListener('click',fn,bool)
如果不传bool值 默认为false,冒泡
如果 bool 值为 true, 捕获
target 与 currentTarget 区别
e.target
用户操作的元素
e.currentTarget
程序员监听的元素
取消冒泡
捕获不能取消,冒泡可以 e.stopPropagation
中断冒泡
事件委托
原理:DOM元素的事件冒泡
事件委托是JavaScript中常用绑定事件的常用技巧,也叫事件代理(Event Delegation),顾名思义,“事件委托”是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。
事件委托的优点
<ul id="list">
<li>click1</li>
<li>click2</li>
<li>click3</li>
<!-- ... -->
<li>clickn</li>
</ul>
复制代码
- 可以节省内存占用,减少事件注册(比如在ul上代理所有li的click事件)
如上面代码所示,如果给每个 <li>
列表项都绑定一个函数,那对内存的消耗是非常大的,因此较好的解决办法就是将<li>
元素的点击事件绑定到它的父元素<ul>
身上,执行事件的时候再去匹配判断目标元素。
- 可以监听动态元素(监听目前不存在的元素的点击事件)
上述的例子中列表项<li>
数量很少,我们给每个列表项都绑定了事件。
在很多时候,我们需要通过用户操作动态的增加或者删除列表项<li>
元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件。
如果用事件委托就没有这种麻烦,因为事件是绑定在父层,和目标元素的增减没有关系,执行到目标元素是在真正响应执行事件函数的过程中匹配的,所以使用事件在动态绑定事件的情况下可以减少很多重复性的工作。
示例:js.jirengu.com/lofareteni/…
setTimeout(()=>{
const button = document.createElement('button')
button.textContent='click1'
div1.appendChild(button)
},1000)
div1.addEventListener('click',(e)=>{
const t =e.target
if(t.tagName.toLowerCase()==='button'){
console.log('button被click')
}
})
复制代码
封装事件委托
示例: js.jirengu.com/wapuposire/…
setTimeout(() => {
const button = document.createElement('button')
button.textContent = 'click1'
div1.appendChild(button)
}, 1000)
on('click', '#div1', '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)) {
//给元素添加一个监听,看当前的target是不是满足selector如果满足调用函数,不满足跳过
fn(e)
}
})
}
复制代码
总结
事件委托就是把事件监听放在祖先元素(如父元素、爷爷元素)上。
好处是:1 节约监听数量 2 可以监听动态生成的元素。
引用了饥人谷教学图片,代码