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

1、事件流

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

1.1 事件冒泡

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

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Document</title>
</head>
<body>
  <div>Click Me</div>
</body>
</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元素的一个事件处理程序属性。如:

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

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

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

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

btn.onclick = null

3.2 DOM2事件处理程序

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

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

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

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

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

let btn = document.getElementById('oBtn')
let handler = function () {
  console.log(this.id)
}
btn.addEventListener('click', handler, false)
btn.removeEventListener('click', handler, false)

3.3 IE事件处理程序

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

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

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

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

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

let btn = document.getElementById('oBtn')
let handler = function () {
  console.log('hello world')
}
btn.attachEvent('onclick', handler)
btn.detachEvent('onclick', handler)

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

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

var EventUtil = {
  // 三个参数:目标元素、事件名、事件处理函数
  addHandler: function(element, type, handler) {
    if (element.addEventListener) {
      element.addEventListener(type, handler, false)
    } else if (element.attachEvent) {
      element.attachEvent("on" + type, handler)
    } else {
      element["on" + type] = handler
    }
  },
  removeHandler: function(element, type, handler) {
    if (element.removeEventListener) {
      element.removeEventListener(type, handler, false)
    } else if (element.detachEvent) {
      element.detachEvent("on" + type, handler)
    } else {
      element["on" + type] = null
    }
  }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值