一、事件

1. EventTarget 接口

DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。

该接口主要提供三个实例方法。

  • addEventListener:绑定事件的监听函数
  • removeEventListener:移除事件的监听函数
  • dispatchEvent:触发事件
  1. // EventTarget.addEventListener()
  2. // EventTarget.addEventListener()用于在当前节点或对象上,定义一个特定事件的监听函数。
  3. // 一旦这个事件发生,就会执行监听函数。该方法没有返回值。
  4. function hello() {
  5. console.log('Hello world');
  6. }
  7. var button = document.getElementById('btn');
  8. button.addEventListener('click', hello, false);
  9. // EventTarget.removeEventListener()
  10. // EventTarget.removeEventListener方法用来移除addEventListener方法添加的事件监听函数。该方法没有返回值。
  11. div.addEventListener('click', listener, false);
  12. div.removeEventListener('click', listener, false);
  13. // EventTarget.dispatchEvent()
  14. // EventTarget.dispatchEvent方法在当前节点上触发指定事件,从而触发监听函数的执行。
  15. // 该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),则返回值为false,否则为true。
  16. var canceled = !cb.dispatchEvent(event);
  17. if (canceled) {
  18. console.log('事件取消');
  19. } else {
  20. console.log('事件未取消');
  21. }

2. 事件模型

监听函数

  • 浏览器的事件模型,就是通过监听函数(listener)对事件做出反应。事件发生后,浏览器监听到了这个事件,就会执行对应的监听函数。这是事件驱动编程模式(event-driven)的主要编程方式。
  • JavaScript 有三种方法,可以为事件绑定监听函数。
  1. // HTML 的 on- 属性
  2. <body onload="doSomething()">
  3. <div onclick="console.log('触发事件')">
  4. // 注意,这些属性的值是将会执行的代码,而不是一个函数。
  5. <!-- 正确 -->
  6. <body onload="doSomething()">
  7. <!-- 错误 -->
  8. <body onload="doSomething">
  9. // 元素节点的事件属性
  10. window.onload = doSomething;
  11. div.onclick = function (event) {
  12. console.log('触发事件');
  13. };
  14. // 注意,这种方法与 HTML 的on-属性的差异是,它的值是函数名(doSomething)
  15. // EventTarget.addEventListener()
  16. // 所有 DOM 节点实例都有addEventListener方法,用来为该节点定义事件的监听函数。
  17. window.addEventListener('load', doSomething, false);

this 指向

  • 监听函数内部的this指向触发事件的那个元素节点。

事件传播

  • 一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
  1. 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
  2. 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
  3. 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
  1. var phases = {
  2. 1: 'capture',
  3. 2: 'target',
  4. 3: 'bubble'
  5. };
  6. var div = document.querySelector('div');
  7. var p = document.querySelector('p');
  8. div.addEventListener('click', callback, true);
  9. p.addEventListener('click', callback, true);
  10. div.addEventListener('click', callback, false);
  11. p.addEventListener('click', callback, false);
  12. function callback(event) {
  13. var tag = event.currentTarget.tagName;
  14. var phase = phases[event.eventPhase];
  15. console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
  16. }
  17. // 点击以后的结果
  18. // Tag: 'DIV'. EventPhase: 'capture'
  19. // Tag: 'P'. EventPhase: 'target'
  20. // Tag: 'P'. EventPhase: 'target'
  21. // Tag: 'DIV'. EventPhase: 'bubble'
  22. // 事件传播的最上层对象是window,接着依次是document,html(document.documentElement)和body(document.body)。
  23. // 也就是说,上例的事件传播顺序,
  24. // 在捕获阶段依次为window、document、html、body、div、p
  25. // 在冒泡阶段依次为p、div、body、html、document、window。

事件的代理

  • 由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)。
  1. // 简单版
  2. var ul = document.querySelector('ul');
  3. ul.addEventListener('click', function (event) {
  4. if (event.target.tagName.toLowerCase() === 'li') {
  5. // some code
  6. }
  7. });

3. Event 对象

事件发生以后,会产生一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。

Event对象本身就是一个构造函数,可以用来生成新的实例。

  1. event = new Event(type, options);
  2. var ev = new Event(
  3. 'look',
  4. {
  5. 'bubbles': true,
  6. 'cancelable': false
  7. }
  8. );
  9. document.dispatchEvent(ev);

实例属性

  1. Event.bubblesEvent.eventPhase
  2. Event.cancelableEvent.cancelBubbleevent.defaultPrevented
  3. Event.currentTargetEvent.target
  4. Event.type
  5. Event.timeStamp
  6. Event.isTrusted
  7. Event.detail
  8. Event.bubbles属性返回一个布尔值,表示当前事件是否会冒泡。该属性为只读属性,一般用来了解 Event 实例是否可以冒泡。
  9. Event.eventPhase属性返回一个整数常量,表示事件目前所处的阶段。该属性只读。
  10. Event.cancelable属性返回一个布尔值,表示事件是否可以取消。该属性为只读属性,一般用来了解 Event 实例的特性。
  11. Event.cancelBubble属性是一个布尔值,如果设为true,相当于执行Event.stopPropagation(),可以阻止事件的传播。
  12. Event.defaultPrevented属性返回一个布尔值,表示该事件是否调用过Event.preventDefault方法。该属性只读。
  13. Event.currentTarget属性返回事件当前所在的节点,即事件当前正在通过的节点,也就是当前正在执行的监听函数所在的那个节点。随着事件的传播,这个属性的值会变。
  14. Event.target属性返回原始触发事件的那个节点,即事件最初发生的节点。这个属性不会随着事件的传播而改变。
  15. Event.type属性返回一个字符串,表示事件类型。事件的类型是在生成事件的时候指定的。该属性只读。
  16. Event.timeStamp属性返回一个毫秒时间戳,表示事件发生的时间。它是相对于网页加载成功开始计算的。
  17. Event.isTrusted属性返回一个布尔值,表示该事件是否由真实的用户行为产生。比如,用户点击链接会产生一个click事件,该事件是用户产生的;Event构造函数生成的事件,则是脚本产生的。
  18. Event.detail属性只有浏览器的 UI (用户界面)事件才具有。该属性返回一个数值,表示事件的某种信息。具体含义与事件类型相关。
  19. 比如,对于clickdblclick事件,Event.detail是鼠标按下的次数(1表示单击,2表示双击,3表示三击);
  20. 对于鼠标滚轮事件,Event.detail是滚轮正向滚动的距离,负值就是负向滚动的距离,返回值总是3的倍数。

实例方法

  1. Event.preventDefault()
  2. Event.stopPropagation()
  3. Event.stopImmediatePropagation()
  4. Event.composedPath()
  5. Event.preventDefault方法取消浏览器对当前事件的默认行为。
  6. stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。
  7. Event.stopImmediatePropagation方法阻止同一个事件的其他监听函数被调用,不管监听函数定义在当前节点还是其他节点。
  8. Event.composedPath()返回一个数组,成员是事件的最底层节点和依次冒泡经过的所有上层节点。
  9. // HTML 代码如下
  10. // <div>
  11. // <p>Hello</p>
  12. // </div>
  13. var div = document.querySelector('div');
  14. var p = document.querySelector('p');
  15. div.addEventListener('click', function (e) {
  16. console.log(e.composedPath());
  17. }, false);
  18. // [p, div, body, html, document, Window]

4. 鼠标事件

鼠标事件种类

  1. // 鼠标事件指与鼠标相关的事件,继承了MouseEvent接口。具体的事件主要有以下一些。
  2. click:按下鼠标(通常是按下主按钮)时触发。
  3. dblclick:在同一个元素上双击鼠标时触发。
  4. mousedown:按下鼠标键时触发。
  5. mouseup:释放按下的鼠标键时触发。
  6. mousemove:当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
  7. mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件。
  8. mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件。
  9. mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件。
  10. mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件。
  11. contextmenu:按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。
  12. wheel:滚动鼠标的滚轮时触发,该事件继承的是WheelEvent接口。

MouseEvent 接口概述

  • MouseEvent接口代表了鼠标相关的事件,单击(click)、双击(dblclick)、松开鼠标键(mouseup)、按下鼠标键(mousedown)等动作,所产生的事件对象都是MouseEvent实例。此外,滚轮事件和拖拉事件也是MouseEvent实例。
  1. // 浏览器原生提供一个MouseEvent构造函数,用于新建一个MouseEvent实例。
  2. var event = new MouseEvent(type, options);
  3. // 第一个参数是字符串,表示事件名称;第二个参数是一个事件配置对象,该参数可选。
  4. // 除了Event接口的实例配置属性,该对象可以配置以下属性,所有属性都是可选的。
  5. screenX:数值,鼠标相对于屏幕的水平位置(单位像素),默认值为0,设置该属性不会移动鼠标。
  6. screenY:数值,鼠标相对于屏幕的垂直位置(单位像素),其他与screenX相同。
  7. clientX:数值,鼠标相对于程序窗口的水平位置(单位像素),默认值为0,设置该属性不会移动鼠标。
  8. clientY:数值,鼠标相对于程序窗口的垂直位置(单位像素),其他与clientX相同。
  9. ctrlKey:布尔值,是否同时按下了 Ctrl 键,默认值为false
  10. shiftKey:布尔值,是否同时按下了 Shift 键,默认值为false
  11. altKey:布尔值,是否同时按下 Alt 键,默认值为false
  12. metaKey:布尔值,是否同时按下 Meta 键,默认值为false
  13. button:数值,表示按下了哪一个鼠标按键,默认值为0,表示按下主键(通常是鼠标的左键)或者当前事件没有定义这个属性;1表示按下辅助键(通常是鼠标的中间键),2表示按下次要键(通常是鼠标的右键)。
  14. buttons:数值,表示按下了鼠标的哪些键,是一个三个比特位的二进制值,默认为0(没有按下任何键)。1(二进制001)表示按下主键(通常是左键),2(二进制010)表示按下次要键(通常是右键),4(二进制100)表示按下辅助键(通常是中间键)。因此,如果返回3(二进制011)就表示同时按下了左键和右键。
  15. relatedTarget:节点对象,表示事件的相关节点,默认为nullmouseentermouseover事件时,表示鼠标刚刚离开的那个元素节点;mouseoutmouseleave事件时,表示鼠标正在进入的那个元素节点。
  16. function simulateClick() {
  17. var event = new MouseEvent('click', {
  18. 'bubbles': true,
  19. 'cancelable': true
  20. });
  21. var cb = document.getElementById('checkbox');
  22. cb.dispatchEvent(event);
  23. }

MouseEvent 接口的实例属性

  1. MouseEvent.altKeyMouseEvent.ctrlKeyMouseEvent.metaKeyMouseEvent.shiftKey
  2. MouseEvent.buttonMouseEvent.buttons
  3. MouseEvent.clientXMouseEvent.clientY
  4. MouseEvent.movementXMouseEvent.movementY
  5. MouseEvent.screenXMouseEvent.screenY
  6. MouseEvent.offsetXMouseEvent.offsetY
  7. MouseEvent.pageXMouseEvent.pageY
  8. MouseEvent.relatedTarget
  9. MouseEvent.altKeyMouseEvent.ctrlKeyMouseEvent.metaKeyMouseEvent.shiftKey
  10. 这四个属性都返回一个布尔值,表示事件发生时,是否按下对应的键。它们都是只读属性。
  11. altKey属性:Alt
  12. ctrlKey属性:Ctrl
  13. metaKey属性:Meta 键(Mac 键盘是一个四瓣的小花,Windows 键盘是 Windows 键)
  14. shiftKey属性:Shift
  15. MouseEvent.button属性返回一个数值,表示事件发生时按下了鼠标的哪个键。该属性只读。
  16. 0:按下主键(通常是左键),或者该事件没有初始化这个属性(比如mousemove事件)。
  17. 1:按下辅助键(通常是中键或者滚轮键)。
  18. 2:按下次键(通常是右键)。
  19. MouseEvent.buttons属性返回一个三个比特位的值,表示同时按下了哪些键。它用来处理同时按下多个鼠标键的情况。该属性只读。
  20. 1:二进制为001(十进制的1),表示按下左键。
  21. 2:二进制为010(十进制的2),表示按下右键。
  22. 4:二进制为100(十进制的4),表示按下中键或滚轮键。
  23. MouseEvent.clientX属性返回鼠标位置相对于浏览器窗口左上角的水平坐标(单位像素)
  24. MouseEvent.clientY属性返回垂直坐标。这两个属性都是只读属性。
  25. MouseEvent.movementX属性返回当前位置与上一个mousemove事件之间的水平距离(单位像素)。
  26. 数值上,它等于下面的计算公式。
  27. currentEvent.movementX = currentEvent.screenX - previousEvent.screenX
  28. MouseEvent.screenX属性返回鼠标位置相对于屏幕左上角的水平坐标(单位像素),MouseEvent.screenY属性返回垂直坐标。
  29. 这两个属性都是只读属性。
  30. MouseEvent.offsetX属性返回鼠标位置与目标节点左侧的padding边缘的水平距离(单位像素),MouseEvent.offsetY属性返回与目标节点上方的padding边缘的垂直距离。
  31. 这两个属性都是只读属性。
  32. MouseEvent.pageX属性返回鼠标位置与文档左侧边缘的距离(单位像素),MouseEvent.pageY属性返回与文档上侧边缘的距离(单位像素)。
  33. 它们的返回值都包括文档不可见的部分。这两个属性都是只读。
  34. MouseEvent.relatedTarget属性返回事件的相关节点。对于那些没有相关节点的事件,该属性返回null
  35. 该属性只读。

MouseEvent 接口的实例方法

  1. MouseEvent.getModifierState方法返回一个布尔值,表示有没有按下特定的功能键。
  2. 它的参数是一个表示功能键的字符串。
  3. document.addEventListener('click', function (e) {
  4. console.log(e.getModifierState('CapsLock'));
  5. }, false);

WheelEvent 接口

  • WheelEvent 接口继承了 MouseEvent 实例,代表鼠标滚轮事件的实例对象。目前,鼠标滚轮相关的事件只有一个wheel事件,用户滚动鼠标的滚轮,就生成这个事件的实例。
  1. var wheelEvent = new WheelEvent(type, options);
  2. 第一个是字符串,表示事件类型,对于滚轮事件来说,这个值目前只能是wheel
  3. 第二个参数是事件的配置对象。该对象的属性除了EventUIEvent的配置属性以外
  4. 还可以接受以下几个属性,所有属性都是可选的。
  5. deltaX:数值,表示滚轮的水平滚动量,默认值是 0.0
  6. deltaY:数值,表示滚轮的垂直滚动量,默认值是 0.0
  7. deltaZ:数值,表示滚轮的 Z 轴滚动量,默认值是 0.0
  8. deltaMode:数值,表示相关的滚动事件的单位,适用于上面三个属性。0表示滚动单位为像素,1表示单位为行,2表示单位为页,默认为0
  9. // 实例属性
  10. WheelEvent.deltaX:数值,表示滚轮的水平滚动量。
  11. WheelEvent.deltaY:数值,表示滚轮的垂直滚动量。
  12. WheelEvent.deltaZ:数值,表示滚轮的 Z 轴滚动量。
  13. WheelEvent.deltaMode:数值,表示上面三个属性的单位,0是像素,1

5. 键盘事件

键盘事件种类

  1. keydown:按下键盘时触发。
  2. keypress:按下有值的键时触发,即按下 CtrlAltShiftMeta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件。
  3. keyup:松开键盘时触发该事件。
  4. 如果用户一直按键不松开,就会连续触发键盘事件,触发的顺序如下。
  5. keydown
  6. keypress
  7. keydown
  8. keypress
  9. ...(重复以上过程)
  10. keyup

KeyboardEvent 接口概述

  1. new KeyboardEvent(type, options)
  2. key:字符串,当前按下的键,默认为空字符串。
  3. code:字符串,表示当前按下的键的字符串形式,默认为空字符串。
  4. location:整数,当前按下的键的位置,默认为0
  5. ctrlKey:布尔值,是否按下 Ctrl 键,默认为false
  6. shiftKey:布尔值,是否按下 Shift 键,默认为false
  7. altKey:布尔值,是否按下 Alt 键,默认为false
  8. metaKey:布尔值,是否按下 Meta 键,默认为false
  9. repeat:布尔值,是否重复按键,默认为false

KeyboardEvent 的实例属性

  1. KeyboardEvent.altKey:是否按下 Alt
  2. KeyboardEvent.ctrlKey:是否按下 Ctrl
  3. KeyboardEvent.metaKey:是否按下 meta 键(Mac 系统是一个四瓣的小花,Windows 系统是 windows 键)
  4. KeyboardEvent.shiftKey:是否按下 Shift
  5. KeyboardEvent.code属性返回一个字符串,表示当前按下的键的字符串形式。该属性只读。
  6. 数字键0 - 9:返回digital0 - digital9
  7. 字母键A - z:返回KeyA - KeyZ
  8. 功能键F1 - F12:返回 F1 - F12
  9. 方向键:返回ArrowDownArrowUpArrowLeftArrowRight
  10. Alt 键:返回AltLeftAltRight
  11. Shift 键:返回ShiftLeftShiftRight
  12. Ctrl 键:返回ControlLeftControlRight
  13. KeyboardEvent.key
  14. 比如 BackspaceTabEnterShiftControlAltCapsLockEscSpacebarPageUpPageDownEndHomeLeftRightUpDownPrintScreenInsertDelWinF1F12NumLockScroll
  15. KeyboardEvent.location
  16. 0:处在键盘的主区域,或者无法判断处于哪一个区域。
  17. 1:处在键盘的左侧,只适用那些有两个位置的键(比如 Ctrl Shift 键)。
  18. 2:处在键盘的右侧,只适用那些有两个位置的键(比如 Ctrl Shift 键)。
  19. 3:处在数字小键盘。
  20. KeyboardEvent.repeat
  21. KeyboardEvent.repeat返回一个布尔值,代表该键是否被按着不放,以便判断是否重复这个键,即浏览器会持续触发keydownkeypress事件,直到用户松开手为止。

KeyboardEvent 的实例方法

  1. KeyboardEvent.getModifierState()方法返回一个布尔值,表示是否按下或激活指定的功能键。它的常用参数如下。
  2. AltAlt
  3. CapsLock:大写锁定键
  4. ControlCtrl
  5. MetaMeta
  6. NumLock:数字键盘开关键
  7. ShiftShift

6. 进度事件

进度事件的种类

  • 进度事件用来描述资源加载的进度,主要由 AJAX 请求、笔记9 - 事件   浏览器模型 - 图1