DOM事件和事件委托

307 阅读3分钟

一、DOM事件

1.什么是DOM?

文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。--MDN

事实上:网页其实是一棵树。

为了让网页实现各种各样的效果,浏览器给window添加了一个document对象,我们将网页抽象成一个document对象,通过这个document对象操作整个页面的所有的节点。上述模型Document Object Model 也就是DOM

2.什么是DOM事件?

DOM事件例子:
* 当用户点击鼠标时
* 当网页加载后
* 当图像加载后
* 当鼠标移至元素上时
* 当输入字段被改变时
* 当 HTML 表单被提交时
* 当用户敲击按键时

事件可以在文档(Document)结构的任何部分被触发,触发者可以是用户操作,也可以是浏览器本身。事件并不是只是在一处被触发和终止;他们在整个document中流动,拥有它们自己的生命周期。

3.DOM事件机制

>>>三个阶段

DOM 事件机制主要分为三个阶段,分别是:捕获阶段目标阶段冒泡阶段

  • 捕获阶段:从window对象传导到目标节点,称为捕获阶段(capture phase)。从外向内找监听函数,叫做事件捕获

  • 目标阶段:在目标节点上触发,称为目标阶段(target phase)。

  • 冒泡阶段:从目标节点传导回window对象,称为冒泡阶段(bubbling phase)。从内向外找监听函数,叫做事件冒泡。 tips:

---浏览器同时支持两种调用顺序,有监听函数就调用,没有则跳过。

---一般情况下先执行捕获,然后再执行冒泡。

>>>举例

对于以下代码,分别给三个div添加click事件监听。

<div class="爷爷">
  <div class="爸爸">
    <div class="儿子">文字</div>
  </div>
</div>

--思考

1.
点击文字,算不算点击儿子?
点击文字,算不算点击爸爸?
点击文字,算不算点击爷爷?
-----------------------
答案:
	都算
2.
那么他们的顺序是什么呢?
-----------------------
答案:
	先是捕获阶段:爷爷->爸爸->儿子
	接着是冒泡阶段:儿子->爸爸->爷爷

>>>事件绑定APIW3C--addEventlistener('click',fn,bool)

* 如果第三个参数bool不传,或者传falsy值, 那么我们会在冒泡阶段调用函数。

* 如果第三个参数bool传值为true, 那么我们会在捕获阶段调用函数。

>>>取消冒泡

捕获不可以取消,但是冒泡可以取消e.propagation()可中断冒泡。但并不是所有事件都可以取消,比如scroll事件,要阻止滚动,可阻止wheel 和 touchstart的默认动作;详情可查询MDN文档--event.bubbles

>>>target vs currentTarget

* `e.target`**用户正在操作的元素**
* `e.currentTarget` **程序员在监听的元素**
<div>
 <span>文字</span>
</div>

假设我们监听的是div, 用户点击文字,那么:

---e.target就是span

---e.currentTarget就是div

>>>特例

div.addEventListener('click',f1)
div.addEventListener('click',f2,ture)

---先执行f1后执行f2,谁先监听,谁先执行。

---在没有父子的情况下,谁先监听,谁先执行。

二、事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

原理

如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其祖先节点上,并让子节点上发生的事件冒泡到祖先节点上,而不是每个子节点单独设置事件监听器。

优点

  • 省监听数,减少内存消耗
<div id=div1>
  <span>span1</span>
  <button>click 1</button>
  <button>click 2</button>
  <button>click 3</button>
</div>

<script>
div1.addEventListener('click', (e)=> { 
  const t = e.target//把目标元素赋给t
  if (t.tagName.toLowerCase() === 'button') {// 判断是否匹配目标元素
    console.log("button 被点击了")
    console.log('button内容是:' + t.textContent);
  }
});
</script>
  • 可监听动态元素
<body>
<div id="div1"></div>
</body>

<script>
setTimeout(()=>{
  const button = document.createElement('button')//div1里面添加一个button
  button.textContent = 'click 1'
  div1.appendChild(button)
},1000) 

  
div1.addEventListener('click',(e)=>{
  const t=e.target
  if (t.tagName.toLowerCase() ==='button'){
  	console.log('button被click')
}
});  
</script>

封装成函数

<body>
	<div id="div1"></div>
</body>

<script>
setTimeout(()=>{
  const button = document.createElement('button')
  button.textContent = 'click 1'
  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)){
            fn(e)//如果操作的元素匹配相对应的选择器,则执行函数
        }
    })
}
</script>