简述DOM 事件机制

一、DOM事件级别

DOM级别一共可以分为四个级别:DOM0级、DOM1级、DOM2级和DOM3级。而DOM事件分为3个级别:DOM 0级事件处理,DOM 2级事件处理和DOM 3级事件处理。由于DOM 1级中没有事件的相关内容,所以没有DOM 1级事件。

1.DOM 0级事件

el.onclick=function(){}

<button id="btn">按钮</button>
// 例1 var btn = document.getElementById('btn'); btn.onclick = function(){ alert(this.innerHTML); }

当希望为同一个元素/标签绑定多个同类型事件的时候(如给上面的这个btn元素绑定3个点击事件),是不被允许的。DOM0事件绑定,给元素的事件行为绑定方法,这些方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的

2.DOM 2级事件

el.addEventListener(event-name, callback, useCapture)

  • event-name: 事件名称,可以是标准的DOM事件
  • callback: 回调函数,当事件触发时,函数会被注入一个参数为当前的事件对象 event
  • useCapture: 默认是false,代表事件句柄在冒泡阶段执行
// 例2
var btn = document.getElementById('btn');
btn.addEventListener("click", test, false);
function test(e){
    e = e || window.event;
    alert((e.target || e.srcElement).innerHTML);
    btn.removeEventListener("click", test)
}

  

3.DOM 3级事件

在DOM 2级事件的基础上添加了更多的事件类型。

  • UI事件,当用户与页面上的元素交互时触发,如:load、scroll
  • 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
  • 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dblclick、mouseup
  • 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
  • 文本事件,当在文档中输入文本时触发,如:textInput
  • 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
  • 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
  • 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
  • 同时DOM3级事件也允许使用者自定义一些事件。

二、DOM事件模型和事件流

DOM事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。

(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段;

(2)目标阶段:真正的目标节点正在处理事件的阶段;

(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。

捕获是从上到下,事件先从window对象,然后再到document(对象),然后是html标签(通过document.documentElement获取html标签),然后是body标签(通过document.body获取body标签),然后按照普通的html结构一层一层往下传,最后到达目标元素。
 

以下是个捕获和冒泡的例子

 

三、事件代理(事件委托)

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)。

1.优点

  • 减少内存消耗,提高性能

假设有一个列表,列表之中有大量的列表项,我们需要在点击每个列表项的时候响应一个事件

// 例4
<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

  

如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。借助事件代理,我们只需要给父容器ul绑定方法即可,这样不管点击的是哪一个后代元素,都会根据冒泡传播的传递机制,把容器的click行为触发,然后把对应的方法执行,根据事件源,我们可以知道点击的是谁,从而完成不同的事。

  • 动态绑定事件

在很多时候,我们需要通过用户操作动态的增删列表项元素,如果一开始给每个子元素绑定事件,那么在列表发生变化时,就需要重新给新增的元素绑定事件,给即将删去的元素解绑事件,如果用事件代理就会省去很多这样麻烦。

2.如何实现

接下来我们来实现上例中父层元素 #list 下的 li 元素的事件委托到它的父层元素上:

// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性处理
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // 判断是否匹配目标元素
  if (target.nodeName.toLocaleLowerCase === 'li') {
    console.log('the content is: ', target.innerHTML);
  }
});

  

四、Event对象常见的应用

event. preventDefault()

  如果调用这个方法,默认事件行为将不再触发。什么是默认事件呢?例如表单一点击提交按钮(submit)跳转页面、a标签默认页面跳转或是锚点定位等。

很多时候我们使用a标签仅仅是想当做一个普通的按钮,点击实现一个功能,不想页面跳转,也不想锚点定位。

event.stopPropagation()

  如果调用这个方法,冒泡阶段将不再触发,捕获阶段依旧。

currentT和target

  •   Event.currentTarget属性返回事件当前所在的节点,即事件当前正在通过的节点,也就是当前正在执行的监听函数所在的那个节点。随着事件的传播,这个属性的值会变。

 

  •   Event.target属性返回原始触发事件的那个节点,即事件最初发生的节点。这个属性不会随着事件的传播而改变。

 

  

 

  • cancelable表示是否支持开发者取消冒泡
  • 如scroll不支持取消冒泡

如何禁用滚动

  •   取消特定元素的wheel和touchstart的默认动作

  

 自定义事件

浏览器自带事件

一共100多种事件,列表在mdn上

提问

开发者能不能在自带事件之外,自定义一个事件

答案:可以,

 

 

 

posted @ 2021-01-04 23:07  时间观测者  阅读(131)  评论(0编辑  收藏  举报