Javascript中的事件流和事件处理程序

Dear 丶 2022-11-11 13:19 334阅读 0赞

1、事件流

Javascript和HTML的交互是通过事件实现的。事件代表文档或浏览器窗口中某个有意义的时刻。事件流描述了页面接收事件的顺序。

1.1 事件冒泡

IE事件流被称为事件冒泡,这是因为事件被定义为从具体的元素(文档树中最深的节点)开始触发,然后向上传播至没那么具体的元素(文档)。比如有以下HTML页面:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>Document</title>
  5. </head>
  6. <body>
  7. <div>Click Me</div>
  8. </body>
  9. </html>

在点击页面中的<div>元素后,最先触发<div>的click事件,然后,click事件沿着DOM树一路向上,顺序如下:
(1)div元素
(2)body元素
(3)html元素
(4)document

1.2 事件捕获

事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。如前面的例子,点击div元素会按照如下顺序触发click事件:
(1)document
(2)html元素
(3)body元素
(4)div元素

2、DOM事件流

DOM2 Events规范规定了事件流分为3个阶段:事件捕获、到达目标和事件冒泡。事件捕获是最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡。

3、事件处理程序

为响应事件而调用的函数被称为事件处理程序。

3.1 DOM0事件处理程序

DOM0中指定事件处理程序的方式是把一个函数赋给DOM元素的一个事件处理程序属性。如:

  1. let btn = document.getElementById('oBtn')
  2. btn.onclick = function () {
  3. console.log('clicked')
  4. }

所赋函数被视为元素的方法,事件处理程序会在元素的作用域中运行,this等于元素:

  1. let btn = document.getElementById('oBtn')
  2. btn.onclick = function () {
  3. console.log(this.id) // oBtn
  4. }

以这种方式添加事件处理程序是注册在事件流的冒泡阶段。
通过将事件处理程序属性的值设置为null,可以移除以DOM0方式添加的事件处理程序:

  1. btn.onclick = null

3.2 DOM2事件处理程序

DOM2 Events为事件处理程序的赋值和移除定义了两个:addEventListener()removeEventListener()。方法接收三个参数:事件名、事件处理函数和一个布尔值,布尔值为true表示在捕获阶段调用事件处理程序,false(默认)表示在冒泡阶段调用。这个事件处理程序同样在被附加到的元素的作用域中运行。

  1. let btn = document.getElementById('oBtn')
  2. btn.addEventListener('click', () => {
  3. console.log(this.id)
  4. }, false)

使用DOM2方式的主要优势是可以为同一个事件添加多个事件处理程序。

  1. let btn = document.getElementById('oBtn')
  2. btn.addEventListener('click', () => {
  3. console.log(this.id)
  4. }, false)
  5. btn.addEventListener('click', () => {
  6. console.log('hello world!')
  7. }, false)
  8. // 多个事件处理程序会以添加的顺序触发

通过addEventListener()添加的事件处理程序只能使用removeEventListener()并传入与添加时同样的参数来移除。意味着使用addEventListener()添加的匿名函数无法移除。

  1. let btn = document.getElementById('oBtn')
  2. let handler = function () {
  3. console.log(this.id)
  4. }
  5. btn.addEventListener('click', handler, false)
  6. btn.removeEventListener('click', handler, false)

3.3 IE事件处理程序

IE实现了与DOM类似的方法,即attachEvent()detachEvent()。两个方法接收同样的参数:事件处理程序的名字和事件处理函数。使用attachEvent()添加的事件处理程序会添加到冒泡阶段。

  1. let btn = document.getElementById('oBtn')
  2. btn.attachEvent('onclick', function () { // 注意参数时onclick
  3. console.log('hello world')
  4. })

在IE中使用attachEvent()与使用DOM0方式的主要区别是事件处理函数的作用域。使用DOM0方式时,事件处理程序中的this值等于目标元素。使用attachEvent()时事件处理函数会在全局作用域中运行,因此this等于window
attachEvent()也可以给一个元素添加多个事件处理程序:

  1. let btn = document.getElementById('oBtn')
  2. btn.attachEvent('onclick', function () {
  3. console.log('hello world')
  4. })
  5. btn.attachEvent('onclick', function () {
  6. console.log(this)
  7. })
  8. // 添加的多个事件处理程序会以添加它们的顺序反向触发。

使用attachEvent()添加的事件处理程序将使用detachEvent()来移除,同样添加的匿名函数无法移除:

  1. let btn = document.getElementById('oBtn')
  2. let handler = function () {
  3. console.log('hello world')
  4. }
  5. btn.attachEvent('onclick', handler)
  6. btn.detachEvent('onclick', handler)

4、跨浏览器事件处理程序

跨浏览器兼容的方式处理事件,需要根据需要分别使用DOM0方式,DOM2方式或IE方式来添加事件处理程序。可以用EventUtil对象来实现:

  1. var EventUtil = {
  2. // 三个参数:目标元素、事件名、事件处理函数
  3. addHandler: function(element, type, handler) {
  4. if (element.addEventListener) {
  5. element.addEventListener(type, handler, false)
  6. } else if (element.attachEvent) {
  7. element.attachEvent("on" + type, handler)
  8. } else {
  9. element["on" + type] = handler
  10. }
  11. },
  12. removeHandler: function(element, type, handler) {
  13. if (element.removeEventListener) {
  14. element.removeEventListener(type, handler, false)
  15. } else if (element.detachEvent) {
  16. element.detachEvent("on" + type, handler)
  17. } else {
  18. element["on" + type] = null
  19. }
  20. }
  21. }

这里的addHandler()和removeHandler()方法并没有解决所有跨浏览器一致性问题,比如IE的作用域问题、多个事件处理程序执行顺序问题等,另外,DOM0只支持给一个事件添加一个处理程序。好在DOM0浏览器已经很少有人用了,所以影响应该不大。

发表评论

表情:
评论列表 (有 0 条评论,334人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JavaScript事件

    1、事件的传播分两种情况: 一种是事件的冒泡,一种是事件的捕获。 2、W3C规定的dom标准事件流将事件的传播分三个阶段: 第一个阶段是捕获阶段,第二个阶段是目标阶段,...