DOM事件

事件就是onClick ,onMouseIn,onMouseOut等、也就是文档或浏览器窗口中发生的一些特定的交互瞬间。
DOM事件派发流程表
image.png
DOM事件分为两种:
1、DOM level0
通过对象属性将一个函数值指定为事件处理程序的做法。也就是将要添加的事件处理程序直接赋给该对象的事件处理程序属性。任何对象只允许一个事件处理程序
2、DOM level2
经过W3C认证的事件,将事件的传播方式分为三个阶段(事件捕获阶段、目标阶段、冒泡阶段)
level 2事件通过EventTarget.addEventListener() 方法将指定的监听器注册到EventTarget上,当该对象触发指定的事件时,指定的回调函数就会被执行。该方法允许给一个事件注册多个 listener。

捕获阶段:先由文档的根节点document往事件触发对象,从外向内捕获事件对象;
目标阶段:到达目标事件位置(事发地),触发事件;
冒泡阶段:再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象。

DOM事件捕获、冒泡

  1. <div id="level1">
  2. <div id="level2">
  3. <div id="level3">
  4. level
  5. </div>
  6. </div>
  7. </div>

对三个div分别进行事件监听(函数分别命名为 f1 f2 f3)时,点击“level”时,既可以算点击level1,也可以算点击level2,还可以算点击了level3。

对于这三个div的事件监听函数进行调用的时候
在IE 5 的时代时,IE认为调用顺序是从里向外 ====》 f3>>f2>>f1 子级元素先触发然后父级元素
而网景则认为调用顺序是从外及里 ======》 f1>>f2>>f3 父级元素先触发然后子级元素

2020年W3C发布标准,规定浏览器同时支持两种调用方式。
首先,有外向内找监听函数(事件捕获)然后从内向外找监听函数(事件冒泡)

  1. X.attachEvent(‘onclick’, fn) // IE:事件冒泡
  2. X.addEventListener(‘click’, fn) //网景:事件捕获
  3. X.addEventListener(‘click’, fn, bool)
  4. //bool可分为两种,如果进行传参(true)则为事件捕获,不传参或者传参(flasy)则为事件冒泡

在特殊情况下,如只监听单个元素事件时,谁先监听 谁先执行

取消默认事件 、取消冒泡

w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;·
preventDefault它是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为。既然是说默认行为,当然是元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用当然就无效了。什么元素有默认行为呢?如链接,提交按钮等。当Event 对象的 cancelable为false时,表示没有默认行为,这时即使有默认行为,调用preventDefault也是不会起作用的。
对于阻止事件冒泡,可使用事件对象中的 X.stopPropagation 进行阻止事件冒泡,但不是所有事件都可以取消冒泡。有些事件不可取消冒泡(可通过MDN进行查询)
image.png
Bubbles 判定 是否可以事件冒泡;Cancelable判定是否可以阻止冒泡事件

DOM事件委托

事件委托就是当事件发生时,把要做的事情委托给父元素(或父元素的父元素)来进行处理。
通过利用冒泡的原理,把事件加到父元素上,通过判断事件来源的子集执行相对的操作。使用事件委托可以避免对某一个事件的每个节点进行事件监听。(减少事件注册,节省内存)
由于事件委托给了父级元素,只要是子级元素都可以代理。这样就可以为之后新增加的子级元素自动添加事件

  1. <ul id="ul1"> //实现功能 点击li打印出1
  2. <li>111</li>
  3. <li>222</li>
  4. <li>333</li>
  5. <li>444</li>
  6. </ul>
  7. //使用遍历
  8. window.onload = function () {
  9. let oUl = document.getElementById("ul1");
  10. let oLi = oUl.getElementsByTagName('li');
  11. for (let i = 0; i < aLi.length; i++) {
  12. aLi[i].onclick = function () {
  13. console.log('1')
  14. }
  15. }
  16. }
  17. //使用事件委托
  18. window.onload = function(){
  19.   let oUl = document.getElementById("ul1");
  20.   oUl.onclick = function(ev){
  21.     var ev = ev || window.event;
  22.     let target = ev.target || ev.srcElement;
  23.     if(target.nodeName.toLowerCase() == 'li'){
  24.          console.log('1')
  25.     }
  26.   }
  27. }

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement。此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名。

如果每个li被点击的效果都不一样,也可以使用事件委托进行

  1. <div id="box">
  2. <input type="button" id="add" value="添加" />
  3. <input type="button" id="remove" value="删除" />
  4. <input type="button" id="move" value="移动" />
  5. <input type="button" id="select" value="选择" />
  6. </div>
  7. //使用事件委托
  8. window.onload = function () {
  9. var oBox = document.getElementById("box");
  10. oBox.onclick = function (ev) {
  11. var ev = ev || window.event;
  12. var target = ev.target || ev.srcElement;
  13. if (target.nodeName.toLocaleLowerCase() == 'input') {
  14. switch (target.id) {
  15. case 'add':
  16. alert('添加');
  17. break;
  18. case 'remove':
  19. alert('删除');
  20. break;
  21. case 'move':
  22. alert('移动');
  23. break;
  24. case 'select':
  25. alert('选择');
  26. break;
  27. }
  28. }
  29. }
  30. }

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。