JavaScript 和 HTML 交互是通过事件实现, 而事件处理程序订阅事件从而执行相应的操作.

事件流

事件流描述的是 从页面接收事件的顺序.
事件冒泡: 从最具体的元素到最不具体的元素(从内到外); — IE
事件捕获: 从最不具体的元素到最具体的元素(从外到内); — Netscape
DOM 事件流: 先捕获后冒泡的方式.

DOM事件流包括三个阶段:

  1. 捕获阶段 (IE 低版本不支持该阶段)
  2. 目标阶段 (触发事件)
  3. 冒泡阶段 (回传文档)

事件处理程序

事件是用户或浏览器自身执行的动作, 事件处理程序(事件监听器)是响应事件的动作. 比如 click 事件的事件处理程序就是 onclick, 为事件指定处理程序的方式也有几种.

HTML 事件处理程序

在标签上绑定事件处理程序, HTML 和 js 紧密耦合

1
<button onclick='alert('clicked')'>click me</button>

DOM 0级事件处理程序

将一个函数赋值给一个事件处理程序属性

1
2
3
4
var btn = document.querySelector('#btn');
btn.onclick = function () {
alert('clicked')
}

DOM 2级事件处理程序

定义了两个方法, 用于绑定事件处理程序和删除事件处理程序: addEventListener()removeEventListener(). 所有的 DOM 节点都包含这两个方法, 方法有三个参数: 需要处理的事件名/ 处理函数/ 布尔值(true 为在捕获阶段调用; false 为在冒泡阶段调用)

1
2
3
4
5
6
7
8
9
var btn = document.querySelector('#btn');
// 绑定处理程序
btn.addEventListener('click', function () {
alert('clicked')
}, false);
// 移除处理程序(移除处理程序时, 第二个参数必须和绑定时一样)
btn.removeEventListener('click', function () {
alert('remove')
}, false)

IE 和 DOM2级事件处理程序的区别

  • IE 事件处理程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var btn = docuemnt.querySelector('#btn');
    // 绑定事件
    btn.attachEvent('onclick', function () {
    // this === window
    alert('click on IE')
    })
    // 移除事件
    btn.detachEvent('onclick', function () {
    alert('remove on IE')
    })

以上两种事件处理程序都支持绑定多个事件, DOM2级按照顺序

  • 跨浏览器事件处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    const EventUtil = {
    // 绑定事件处理程序
    addHanlder: 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;
    }
    }
    }

事件代理/ 委托

由于事件处理程序越多, 性能越差, 所以有了事件处理程序. 事件代理的原理是基于事件的冒泡机制实现的, 由上级节点代理完成事件绑定, 优点是:

节省内存占用, 减少事件注册. 比如在ul代理所有li的点击事件
动态新增子级时, 无需再次绑定事件.

可能会提到currentTargettarget的区别: currentTarget 是注册事件时的目标(在事件处理程序中this始终指向 currentTarget), target 是真正触发事件的目标.

1
2
3
4
5
6
7
document.body.onclick = function (event) {
// 事件注册时的元素
console.log(document.body === event.currentTarget); // true
console.log(this === document.body); // true
// 真正点击的元素
console.log(event.target === document.querySelector('#btn')); // true
}

事件对象

事件内置的 event 对象, 下面列举常用的几个事件对象属性:

  • event || window.event
  • event.preventDefault() || window.event.cancelBubble(Boolean)
    阻止浏览器默认事件
  • event.stopPropagation() || window.event.returnValue(Boolean)
    阻止冒泡事件
  • event.target || winow.event.srcElement

Created on 2017-12-10 by Cara