事件

事件流

DOM事件流

DOM2级事件规定事件流包括三个阶段: 事件捕获阶段, 处于目标阶段和事件冒泡阶段. IE9+的等浏览器都支持此事件流

事件处理程序

DOM0级事件处理程序

1
2
3
4
5
6
var div = document.getElementById("div1");
div.onclick = function() {
console.log(this.id);//div1
}

div.onclick = null;//删除事件

DOM2级事件处理程序

  • addEventListener(要处理的事件名, 作为事件处理的函数, 是否捕捉);
  • removeEventListener(要处理的事件名, 作为事件处理的函数, 是否捕捉);
1
2
3
4
5
6
var div = document.getElementById("div1"); 
var handler = function() {
//do something
}
div.addEventListener('click', handler, false);
div.removeEventListener('click', handler, false);

IE事件处理程序

  • attachEvent(事件处理名称, 事件处理函数)
  • detachEvent(事件处理名称, 事件处理函数)

attachEvent绑定的事件需要添加前缀on, 而addEventListener不需要; 事件处理函数的this指向window

1
2
3
4
5
6
var div = document.getElementById("div1"); 
var handler = function() {
console.log(this === window); //true
}
div.attachEvent('onclick', handler);
div.detachEvent('onclick', handler);

事件对象

DOM中的事件

只有在事件处理程序执行期间, event对象才会存在, 一旦事件处理程序执行完成, event对象就会被销毁

当我们将事件挂载在body上, 并点击body中的一个按钮btn, 会有如下结果;

1
2
3
4
5
document.body.onclick = function(event) {
console.log(event.currentTarget === document.body);//true
console.log(this === document.body);//true
console.log(event.target === document.getElementById("btn"));//true
}
  • preventDefault() 阻止默认事件
  • stopPropagation() 阻止事件的捕捉和冒泡
  • eventPhase 值为 1 是捕捉阶段, 值为2是处于目标对象上, 值为3是冒泡阶段

IE中的事件对象

1
2
3
4
5
6
7
var btn = document.getElemntById('btn');
btn.onclick = function() {
window.event.type; //click
window.event.returnValue = false;//阻止默认事件
window.event.cancelBubble = true;//阻止冒泡事件
window.event.scrElemnt == this;//true 事件对象
}

事件类型

UI事件

  • DOMActivate: 表示元素已被用户操作(通过鼠标或键盘)激活. 这个事件被在dom3级事件废弃, 建议不使用
  • load: 当页面完全加载后在window上面触发, 当所有框架都加载完毕时在框架集上面触发, 当图像加载完毕时在元素上面触发, 或者嵌入的内容加载完毕时在<object>元素上面触发
  • unload: 当页面卸载时在window上面触发等, 与load事件对应
  • abort: 用户停止下载过程时, 如果嵌入的内容没有加载完毕时, 则在<object>元素上面触发
  • error: 当发生JavaScript错误时在window上触发, 当无法加载图像时在<img>元素上面触发等
  • select: 用户选择文本框中的字符时触发
  • resize: 窗口大小变化时触发
  • scroll: 滚动条滚动时触发

焦点事件

  • focus: 当元素获取焦点时触发, 这个事件不冒泡
  • focusin: 在元素获取焦点时触发, 这个事件冒泡
  • blur: 当元素失去焦点时触发, 这个事件不冒泡
  • focusout: 在元素失去焦点时触发, 是html事件blur的通用版本

当焦点从一个元素移动到另一个元素时, 会依次触发下列事件, 其中focusout blur DOMFocusOut在失去焦点元素上触发, 其他在获取焦点元素上触发

  • focusout
  • focusin
  • blur
  • DOMFocusOut
  • focus
  • DOMFocusIn

鼠标事件

  • click
  • dbclick
  • mousedown
  • mouseenter
  • mouseleave
  • mousemove
  • mouseout
  • mouseover
  • mouseup

除了 mouseenterh, mouseleave 不冒泡, 其他都冒泡; 事件间也会有关联; 只有在同一个元素上相继触发mousedown和mouseup才会触发click事件, 取消任何一个事件都不会触发click

  1. 客户区坐标位置

    • clientX 鼠标在客户区的水平坐标
    • clientY 鼠标在客户区的垂直坐标
  2. 页面坐标位置

    • pageX
    • pageY

此两值与clientX, clientY类似, 只不过从页面本身而不是从视口的左边和定边开始计算的

1
2
pageX = event.clietX + (docuemnt.body.scrollLeft || document.documentElement.scrollLeft);
pageY = event.clietY + (docuemnt.body.scrollTop || document.documentElement.scrollTop);
  1. 屏幕坐标位置

    • screenX 到屏幕最左侧的坐标
    • screenY 到屏幕最上方的坐标
  2. 相关元素

在嵌套的元素里绑定mouseover, mouseout等事件, 会触发多余事件, 我们可以通过判断相关元素, 来避免多余事件触发, IE9+可以使用event.relatedTarget属性判断, ie8及以下使用event.fromElmentevent.toElement属性判断

1
2
3
4
5
6
7
8
9
10
11
12
13
var EventUtil = {
getRelatedTarget: function(event) {
if (event.relatedTarget) {
return event.relatedTarget;
} else if (event.toElement) {
return event.toElement;
} else if (event.fromElement) {
return event.fromElement;
} else {
return null;
}
}
}
  1. 鼠标按钮

当发生mousedownmouseup事件时, 可以根据event.button来判断按下的是鼠标的哪个键; firefox支持一个DOMMourseScroll事件, 会冒泡到window

  1. 鼠标滚轮事件

滚轮事件mousewheel都会冒泡到documentwindow

1
2
3
$(document).on("mousewheel", function(e) {console.log(e.detail)})
//firefox
$(window).on("DOMMouseScroll", function(e) {console.log("detail:", e.detail)})

键盘与文本事件

  • keydown 任意键都会触发
  • keypress 只要字符才会触发
  • keyup 当用户释放按键时才触发

  • 获取字符编码

1
2
3
4
5
6
7
8
9
var eventUtil = {
getCharCode: function(event) {
if (typeof event.charCode == "number") {
return event.charCode;
} else {
return event.keyCode;
}
}
}
  • getModifierState(“Shift/Control/AltGraph/Meta”) 检测是否被按下
  • textInput事件

只会在可编辑区域触发事件, 与keypress也有区别, keypress在按下影响文本显示时也会触发(比如退格键)

  • event.inputmethod 表示把文本输入到文本框中的方式, 比如黏贴复制, 拖放, 手写等

复合事件

可以让用户输入键盘上没有的字符

  • compositionstart
  • compositionupdate
  • compositionend
1
2
//判断是否支持复合事件
document.implementation.hasFeature("CompositionEvent", "3.0")

变动事件

  • DOMSubtreeModified DOM结构发生任何变化时触发
  • DOMNodeInserted
  • DOMNodeRemoved
  • DOMNodeInsertedIntoDocument 在DOMNodeInserted后触发
  • DOMNodeRemovedFromDocument 在DOMNodeRemoved后触发
  • DOMAttrModified
  • DOMCharacterDataModified 值变化触发

HTML5事件

  1. contextmenu 鼠标右击调出上下文事件, 通常可以用来替换右击显示样式
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
var div = document.getElementById("div");
div.oncontextmenu = function(e) {
e.preventDefault();
//阻止默认显示, 显示自定样式
var menu = document.getElementById("menu");
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
menu.style.visibility = 'visible';
}
```

2. beforeunload 关闭页面前提示是否关闭
3. DOMContentLoaded

> 这个方法与load的事件的区别是: load会等待js, 图片加载完毕, 而这个事件发生在形成完整的DOM树后触发;
对于IE9以前的浏览器不支持此事件, 通过setTimeout(function() {}, 0)实现, 并且作为页面中第一个超时顶用.

4. readystatechange事件
- uninitialized(未初始化) 对象未初始化
- loading(正在加载) 对象正在加载数据
- loaded 对象加载数据完毕
- interactive (交互) 可以操作对象了, 但还没有完全加载 (DOMContentLoaded事件也几乎同时发送在此时)
- complete(完成) 对象已经加载完成

> 不是没有对象的加载, 都会有以上阶段, 有些对象会跳过某些阶段

document.onreadystatechange = function(event) {
if (document.readyState == ‘interactive’ || document.readyState == ‘complete’) {
consolelog(‘content loaded’)
}
}

1
2
3
4
5

5. pageshow和pagehide

- pageshow 页面显示时触发, 不管是否来自缓存, 会在load事件后触发; 可以通过event.persisted来判断, true为来自缓存
- pagehide 在页面卸载时触发, 在unload事件前触发

window.onpageshow = function(e) {console.log(e.persisted)}

1
2

6. hashchange事件

window.onhashchange = function(event) {
console.log(event.oldURL, event.newURL)
}

1
2
3
4
5
6

### 设备事件
1. orientationchange 事件, ios设备屏幕旋转时会触发
- window.orientation值为0, 表示刚好垂直
- window.orientation值为90, 表示头朝正左边
- window.orientation值为-90, 表示头朝正右边

window.orientationchange = function(event) {
console.log(window.orientation)
}

1
2

2. MozOrientation 事件 安卓 设备支持

window.MozOrientation = function(event) {
consoe.log(event.x, event.y, event.y)
}

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
27
28
29
30
31
32
33
34
35

3. deviceorientation 事件 与MozOrientation类似 兼容性也是最好的
- alpha: z轴 0 - 360度
- beta: x轴 -180 - 180度
- gamma: y轴 -90 - 90度
- absolute 布尔值, 表示设备是否返回一个绝对值
- compassCalibrated 布尔值, 表示指南针是否校准过

4. devicemotion 事件 可以检测手机是否在往下掉, 是否被拿在手里
- acceleration: 一个包含x, y, z的对象, 在不考虑重力的情况下, 告诉你在每个方向上的加速度
- accelerationIncludeingGravity: 一个包含x, y, z的对象, 在考虑重力的情况下, 告诉你在每个方向上的加速度
- interval 以毫秒表示的时间值
- rotationRate: 包含alpha, beta, gamma的对象

### 触摸与手势事件
1. 触摸事件
- touchstart
- touchmove
- touchend
- touchcanel

2. 手势事件
- gesturestart 当一个手指已经按在屏幕上时, 而另一个手指又触摸屏幕时触发
- gesturechange 当触摸屏幕任何一个手指位置变化时触发
- gestureend 当任何一个手指从屏幕上移开时触发

> 只有两个手指都触摸到屏幕上才触发这些事件

## 内存和性能
### 事件委托
> 当对一个元素的子元素绑定了很多事件的时候, 可以对其父元素利用事件委托的机制绑定, 利用`event.target`判断是哪个子元素, 响应对应的事件, 可以提升性能

### 移除事件处理程序
> 在ie8以前的浏览器, 当一个元素绑定了事件, 然后这个元素被删除后, 因为还和事件处于引用关系, 因此还在内存中无法释放, 所以应该先取消引用关系, 再删除;
或是把事件委托给父类




var btn = document.getElementById(“btn”);
btn.onclick = function(event) {
var div = document.getElementById(“div”);
div.innerHTML = ‘clicked’
}

//优化如下
btn.onclick = function(event) {
var div = document.getElementById(“div”);
//取消引用关系
btn.onclick = null;
div.innerHTML = ‘clicked’
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

## 模拟事件
### DOM中的事件模拟
- createEvent(参数)
+ UIEvents 鼠标键盘事件
+ MouseEvents 一般化的鼠标事件
+ MutationEvents 一般化的DOM变动
+ HTMLEvents 一般化的HTML事件

- dispatchEvent(事件对象) 触发事件

1. 鼠标事件
> createEvent("MouseEvents"), 返回对象有个initMouseEvent()方法, 该方法接受如下15个参数

![鼠标事件参数](https://img.alicdn.com/tfs/TB1KMGzSVXXXXcBXFXXXXXXXXXX-1992-1222.png)

var btn = document.getElementById(“myBtn”);
var event = document.createEvent(“MouseEvents”);
event.返回对象有个initMouseEvent(“click”, true)
event.initMouseEvent(“click”, true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
btn.dispatchEvent(event)

1
2
3
4
5
6
7
8
9

2. 键盘事件
- createEvent("keyboardEvent"), 返回对象包含一个initKeyEvent()方法, 可接受如下参数
![鼠标事件参数](https://img.alicdn.com/tfs/TB1UHB_SVXXXXaIapXXXXXXXXXX-2090-788.png)

3. 模拟其他事件
> createEvent("MutationEvents"), 返回对象有个initMutationEvent()方法, 可接受参数, type, bubbles, cancelable, relatedNode, preValue, newValue, attrName, attrChange

> createEvent("HTMLEvents"), 返回对象有个initEvent()方法

var event = docuemnt.createEvent(‘HTMLEvents’);
event.initEvent(“focus”, true, true);
target.dispatchEvent(event);

1
2
3

4. 自定义事件
> createEvent("CustomEvent"), 返回对象有个initCustomEvent()方法, 接受4个参数, type, bubbles, cancelable, detail(对象, 保存在event对象的detail属性中)

event = docuemnt.createEvent(‘CustomEvent’);
event.initCustomEvent(“myevent”, true, true, false, “hello world”);
target.dispatchEvent(event);

1
2

### IE中的事件模拟

var mtBtn = document.getElementById(“myBtn”)
var event = document.createEventObject();
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;

myBtn.fireEvent(“onclick”, event);
```