事件
事件流——事件冒泡流、事件捕获流
事件流描述页面接收事件的顺序
冒泡
结构上(非视觉上)嵌套关系的元素会存在冒泡事件功能,及同一事件,子元素向父元素冒泡
捕获
IE 没有
将 addEventListener 第三个参数设置 true
结构上(非视觉上)嵌套关系的元素会存在捕获事件功能,及同一事件,父元素向子元素捕获
DOM 事件流
DOM2 Event 规范规定事件流分三阶段:事件捕获、到达目标、事件冒泡
通常不认为到达目标后的事件处理阶段不属于事件捕获,而被认为是冒泡阶段的一部分
现代浏览器会在捕获阶段在事件目标上触发事件,虽然 DOM2 Events 规范捕获阶段不命中事件目标,所以就导致在时间目标上有两个机会来处理事件
执行顺序:先捕获后冒泡
wrapper.addEventListener(
"click",
function () {
console.log("wrappermp");
},
false
);
content.addEventListener(
"click",
function () {
console.log("contentmp");
},
false
);
box.addEventListener(
"click",
function () {
console.log("boxmp");
},
false
);
wrapper.addEventListener(
"click",
function () {
console.log("wrapperbh");
},
true
);
content.addEventListener(
"click",
function () {
console.log("contentbh");
},
true
);
box.addEventListener(
"click",
function () {
console.log("boxbh");
},
true
);
不冒泡事件
focus、blur、change、submit、reset、select 等
取消冒泡
1、调用事件对象函数e.stopPropagation()
IE9 以下版本不支持
2、配置事件对象属性e.cancelBubble = true
IE 支持
封装取消冒泡函数 stopBubble
function stopBubble(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
事件处理程序
为响应事件而调用的程序叫做事件处理程序,通常以“on”开头
HTML 事件处理程序
属性为事件处理程序,属性值为 js 代码
为了避免使用 HTML 实体,建议使用单引号代替双引号,不然相关字符就要使用转义字符来代替(如‘ < ’在双引号中要用‘ < ’)
这种方式指定的事件处理程序会创建一个函数来封装属性值呃,这个函数有一个特殊的局部变量 event,其中保存的就是 event 对象;这个函数中,this 值相当于时间目标的元素;另外这个函数作用域链被扩展了,document 和元素自身的成员都可以被当成局部变量来访问,这是通过 with 实现的,等价于:
function() { with(document) { with(this) { //事件处理程序属性值 } }}
如果这个元素是一个表单输入框,则作用域链还会包含表单元素,等价于:
function() { with(document) { with(this.form) { with(this) { //事件处理程序属性值 } } }}
使用 HTML 事件处理程序的问题有:1、时机问题(事件处理程序绑定的函数在用户点击时还未定义)2、HTML 和 js 强耦合,有时修改需要修改两处代码
DOM0 事件处理程序
通过在 js 中对元素相关事件处理程序属性赋值函数来添加
这种添加方式在相关代码运行之后才会给相关元素添加,事件处理程序属性赋值的函数会被认为是元素的方法,会在元素的作用域中运行,所以 this 指向该元素
这种方法添加的事件处理程序是注册在事件流的冒泡阶段的
通过给事件处理程序属性的值修改为 null,可以移除 DOM0 方式添加的事件处理程序
如果事件是通过 HTML 定义的,则在 js 中元素相关属性值是一个包装相应 HTML 事件处理程序属性值的函数
DOM2 事件处理程序
DOM2 Event 为事件处理程序添加和移除定义了两个方法:addEventListener()、removeEventListener()
这两个方法暴露在所有 DOM 节点上,它们接收三个参数:事件名(不需要加 ” on “)、事件处理函数、布尔值(true 表示在捕获阶段调用事件处理程序,false 表示在冒泡阶段调用事件处理程序)
该方式添加的事件处理程序同样在被附加到的元素作用域中运行,而且 DOM2 方法优势在于可以在同一个事件上添加多个处理程序
但是 DOM2 移除事件处理程序只能传入其添加时同样的参数才能移除,同样的事件、处理函数(引用值相同)、相同的布尔值
所以匿名函数添加后是无法移除的
IE 事件处理程序
attachEvent()、detachEvent()
接收两个参数:事件处理程序名(包含 “ on ”)、事件处理函数
这种方法添加的事件处理程序是在全局作用域中运行的,所以 this 等于 window
同样的可以使用该方法对一个事件添加多个处理函数,但是移除函数同样要提供相同的参数
总结绑定事件方法
1、elem.onXXX = function() {}
行间 onXXX = ""
兼容性很好
执行时 this 是本身
2、dom.addEventListener(事件类型,处理函数,false)
IE9 一下不兼容,可以给一个对象的一个事件绑定多个处理函数,同一个函数只能绑定一次
执行时 this 是本身
第三个参数决定了事件是以冒泡还是捕获模型处理,false 为冒泡,true 为捕获
3、dom.attachEvent('onXXX',处理函数)
IE 独有,可以给一个对象的一个事件绑定多个处理函数,并且可以绑定多次同一个函数
执行时 this 指向 window——解决方案
div.attachEvent('onclick', function() { test.call(div);})function test() { //...}
封装 addEvent 方法
function addEvent(elem, type, fn) {
if ((elem, addEventListener)) {
elem.addEventListener(type, fn, false);
} else if (elem.attachEvent) {
elem.attachEvent("on" + type, function () {
fn.call(elem);
});
} else {
elem["on" + type] = fn;
}
}
总结解除事件
1、dom.onXXX = false/""/null;
2、dom.removeEventListener(事件类型,处理函数,false)
事件类型和处理函数都要是同一个引用,所以匿名函数清除不了
3、dom.detachEvent('on' + type, fn)
事件类型和处理函数都要是同一个引用,所以匿名函数清除不了
封装 removeEvent 方法
function removeEvent(elem, type, fn) {
if (elem.removeEventListener) {
elem.removeEventListener(type, fn, false);
} else {
elem["on" + type] = null;
}
}
事件对象
div.onclick = function(e) { //IE的e无效,所以我们适配IE需要用它的window.event var event = e || window.event;}
在 DOM 合规的浏览器中,event 对象是传给事件处理程序的唯一参数,不管是使用 HTML、DOM0 还是 DOM2 方法绑定的事件,都会在事件处理函数中传入 event 对象参数;event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁
事件对象都会有以下公共属性和方法:
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
bubbles | Boolean | R | 表示事件是否冒泡 |
cancelable | Boolean | R | 表示是否可以取消事件的默认行为 |
currentTarget | Element | R | 当前事件处理程序所在的元素 |
defaultPrevented | Boolean | R | true 表示已调用 preventDefault()方法(DOM3 新增) |
detail | Integer | R | 事件相关的其他信息 |
eventPhase | Integer | R | 表示事件处理程序阶段:1-捕获、2-到达目标、3-冒泡 |
preventDefault() | Function | R | 用于取消事件的默认行为,只有 cancelable 为 true 才能调用 |
stopImmediatePropagation() | Function | R | 用于取消所有后续事件捕获或冒泡并阻止调用任何后续事件处理程序(DOM3 新增) |
stopPropagation() | Function | R | 用于取消所有后续事件捕获或冒泡,只有 bubbles 为 true 才可以调用 |
target | Element | R | 事件目标 |
trusted | Boolean | R | true 表示事件是由浏览器生成的,false 表示事件是由开发者通过 js 创建的(DOM3 新增) |
type | String | R | 被触发的事件类型 |
View | AbstractView | R | 与事件相关的抽象视图,等于事件所发生的 window 对象 |
在事件处理程序内部,this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际目标
比如 body 上有一个按钮 button,然后 body 添加了点击事件,如果点击按钮,则点击事件会冒泡触发 body 上的点击事件,这时候 this 和 currentTarget 都等于 body,但是 target 等于 button
type 在一个事件处理函数处理拆分事件时很有用,例如可以先创建事件处理函数用 switch(type)捕捉 click、mouseout、mouseover,然后将事件处理函数直接绑定到三个不同事件上,就可以实现三个不同功能
let handler = function (event) {
switch (event.type) {
case "click":
//...
break;
case "mouseover":
//...
break;
case "mouseout":
//...
break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
IE 事件对象
IE 事件对象是根据事件处理程序被指定的方式不同而访问 event 不同,如果是 DOM0 方式指定,则 event 是 window 的一个属性;如果是使用 attachEvent()指定的,则会像其他正常浏览器一样作为参数传给处理函数(但是 event 也还是 window 的属性)
IE 事件对象都会包含以下公共属性方法:
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
cancelBubble | Boolean | RW | 默认为 false,设置为 true 可以取消冒泡 |
returnValue | Boolean | RW | 默认为 true,设置为 false 可以取消默认事件行为 |
srcElement | Element | R | 事件目标 |
type | String | R | 触发的事件类型 |
在 DOM0 方式指定的事件处理程序中,srcElement 等于 this,但是在 attachEvent()方法指定的事件处理程序中,this 与 srcElement 不相等
事件目标
1、event.target
,火狐只有这个
2、event.srcElement
,IE 只有这个
上述俩谷歌都有
div.onclick = function (e) {
var event = e || window.event;
var target = event.target || event.srcElement;
};
事件委托
利用冒泡和事件源对象进行处理
<ul>
{" "}
<li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li>
</ul> //不需要子元素一个个绑定事件,直接通过冒泡事件从父元素的onclick事件中取得被点击的子元素var ul = document.getElementsByTagName('ul')[0];ul.onclick = function(e) { var event = e || window.event; var target = event.target || event.srcElement; console.log(target.innerText);}
阻止默认事件
1、return false
以对象属性注册的事件才有效
dom.onclick = function () {
return false;
};
2、事件对象方法e.preventDefault()
会将 cancelable 属性设置为 true
IE9 以下不兼容
3、事件对象属性e.returnValue = false
IE 兼容
封装阻止默认事件 cancelHandler 方法
function cancelHandler(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
e.returnValue = false;
}
}
阻止事件流
stopPropagation()方法用于阻止事件流在 DOM 中传播,取消后续的事件捕获或冒泡
let btn = document.getElementById('btn');btn.onclick = function(event) { console.log('btn'); stopPropagation();}document.body.onclick = function(event) { console.log('body');}
所以这里只打印了 btn
事件类型
DOM3 Event 定义了如下事件类型:
用户界面事件(UIEvent),涉及与 BOM 交互的通用浏览器事件
焦点事件(FocusEvent),元素获取或失去焦点时触发
鼠标事件(MouseEvent),使用鼠标在界面上执行某些操作触发
滚轮事件(WheelEvent),使用鼠标滚轮时触发
输入事件(InputEvent),向文档中输入文本时触发
键盘事件(KeyboardEvent),使用键盘在页面上执行某些操作时触发
合成事件(CompositionEvent),使用某种 IME(Input Method Editor)输入字符时触发
同时浏览器还保留了一些 DOM 和 BOM 上的专有事件,不同浏览器实现不同
用户界面事件
用户界面事件和 UI 事件不一定和用户操作有关;UI 事件主要有几种:
DOMActivate:元素被用户通过鼠标或键盘操作激活时触发(在 DOM3 Event 中已经被废弃,因为浏览器实现存在差异,所以不要使用)
load:在 window 上当页面加载完成后触发,在窗套上当所有窗格都加载完成后触发,在<img>元素上当图片加载完成后触发,在<object>元素上当相应对象加载完成后触发
unload:在 window 上当页面完全卸载后触发,在窗套上当所有窗格都卸载完成后触发,在<object>元素上当相应对象卸载完成后触发
abort:在<object>元素上当相应对象加载完成前被用户提前终止下载时触发
error:在 window 上当 js 报错时触发,在<img>元素上当无法加载指定图片时触发,在<object>元素上当无法加载相应对象时触发,在窗套上当一个或多个窗格无法加载完成时触发
select:在文本框上当用户选择了一个或多个字符时触发
resize:在 window 窗格上当窗口或窗格被缩放时触发
scroll:当用户滚动包含滚动条的元素时在元素上触发,<body>元素包含已加载页面的滚动条
除了 DOMActivate,这些事件在 DOM2 Events 中都被归为 HTML Events
1、load
在 window 对象上,load 事件会在整个页面(包括所有外部资源如图片、js 文件和 css 文件)加载完成后触发
第一种添加方式是使用 js 添加,例如 addEventListener(),这个事件 event.target 会是 document,IE8 及更早版本中 srcElement 不会有这个属性
第二种方法是在 body 元素上添加 onload 属性
一般来说,任何在 window 上发生的事件都能通过给 body 元素上添加对应属性赋值来指定,因为 HTML 没有 window 元素,但实际开发中尽量使用 js 方式
根据 DOM2 Events,load 事件应该在 document 而非 widow 上触发,为了向后兼容,所有浏览器都在 window 上实现了 load 事件
在图片上,包括 DOM 中的图片和非 DOM 中的图片,可以在 HTML 中直接给<img>元素的 onload 属性指定事件处理程序;也可以在 js 里添加事件处理程序;同样,处理程序也会接收到 event 对象;加载图片不一定是将 img 元素添加到文档只要给它设置了 src 属性就会立即开始下载
script 元素也能绑定 load 事件,IE 以及更早版本不支持 script 元素触发 load 事件,在 script 添加到文档之前不会下载 script 文件
link 元素在 IE 和 Opera 上支持触发 load 事件,link 在添加到文档之前不会下载 link 文件
2、unload
与 load 事件相对,unload 事件会在文档卸载完成后触发,一般是在一个页面导航到另一个页面时触发,用于清理引用,以免内存泄漏
第一种方式是通过 js 添加,这种方式生成的 event 对象在 DOM 合规的浏览器中只有 target 属性(值为 document),IE8 及更早版本在这个事件上不提供 srcElement;第二种方式是给 body 元素添加 unload 属性
unload 事件应该在 document 而不是 window 上触发,但是为了向后兼容,所有浏览器在 window 上也实现了 unload 事件
3、resize
当浏览器窗口被缩放到新高度或宽度时,会触发 resize,这个事件在 window 上触发,因此可以通过 js 或者在 body 上添加 onresize 属性来指定事件处理程序
这个事件也会生成 event 对象,而且这个对象 target 属性 DOM 合规的浏览器中时 document,而 IE8 及更早版本中没有提供可用属性
不同浏览器触发情况不同,IE、Safari、Chrome、Opera 会在窗口缩放超过 1 像素时触发 resize 事件,然后随着浏览器窗口变化不断触发,Firefox 早期版本只会在用户停止缩放浏览器时触发
窗口在最大化和最小化时也会触发 resize 事件
4、scroll 事件
虽然 scroll 事件发生在 window 上,但是它反映的是页面中相应元素的变化
在混杂模式下,可以通过 body 元素 scrollLeft 和 scrollTop 属性的变化;而在标准模式下,这些变化在除早期版本的 Safari 之外的所有浏览器中都发生在 html 元素上
window.addEventListener("scroll", (event) => { if (document.compatMode == "CSS1Compat") { //document.documentElement.scrollTop } else { //document.body.scrollTop }});
scroll 事件也会随着文档滚动而重复触发
焦点事件
焦点事件在页面获得或失去焦点时触发,焦点事件有以下 6 中:
blur:当元素失去焦点时触发,这个事件不冒泡
DOMFocusIn:当元素获得焦点时触发,是 focus 的冒泡版,只有 Opera 实现了,DOM3 Events 废弃了这个,推荐 focusIn
DOMFocusOut:当元素失去焦点触发,是 blur 的通用版,只有 Opera 实现了,DOM3 Events 废弃了这个,推荐 focusout
focus:当元素获得焦点时触发,这个事件不冒泡
focusin:当元素获得焦点时触发,focus 的冒泡版
focusout:当元素失去焦点时触发,blur 的通用版
因为这两个事件不冒泡,所以 IE 增加了 focusin 和 focusout,后来被 DOM3 Events 标准化
当焦点从页面中的一个元素转移到另一个元素上时,会依次发生如下事件:
focusout,在失去焦点的元素上触发
focusin,在获得焦点的元素上触发
blur,在失去焦点的元素上触发
DOMFocusOut 在失去焦点的元素上触发
focus 在获得焦点的元素上触发
DOMFocusIn,在获得焦点的元素上触发
鼠标和滚轮事件
DOM3 Events 定义了 9 种鼠标事件:
click,用户按住鼠标左键或按回车后触发
dblclick,在用户双击鼠标左键时触发
mousedown,在用户按下任意鼠标键时触发
mouseenter,用户把鼠标光标从元素外部移到元素内部时触发;这个事件不冒泡,也不会在光标经过后代元素时触发
mouseleave,用户把鼠标光标移到元素外部时触发;这个事件不冒泡,也不会在光标经过后代元素时触发
mousemove,在鼠标光标在元素上移动时反复触发
mouseout,用户把鼠标光标从一个元素移动到另一个元素时触发,移动到的元素可以是原始元素的外部元素也可以是其子元素
mouseover,用户把鼠标光标从元素外部移到元素内部时触发
mouseup,在用户释放鼠标键时触发
mousewheel,触发鼠标滚轮或类似设备上的滚轮交互
除了 mouseenter 和 mouseleave,所有鼠标事件都会冒泡,都可以被取消
click 事件触发顺序:mousedown、mouseup、click
dblclick 事件触发顺序:整个 click 事件、整个 click 事件、dblclick
上述默认事件如果取消其中某一个前提,都会导致最后事件无法触发
1、客户端坐标
鼠标事件都是在浏览器视口中的某个位置上发生的,这些信息被保存在 event 中的 clientX 和 clientY 属性中
这个两个距离不用考虑页面滚动,因为它们是根据浏览器视口来决定位置的
2、页面坐标
通过 event 的 pageX 和 pageY 获取
这两个距离都是鼠标光标在页面上的位置,而不是视口的位置
3、屏幕坐标
通过 event 的 screenX 和 screenY 属性获取
这两个距离都是鼠标光标在整个屏幕上的位置,不是页面也不是视口
4、修饰键
键盘上的修饰键在 DOM 中有规定:
Shift——shiftKey
Ctrl——ctrlKey
Alt——altKey
Meta——metaKey
这几个属性会在对应的键位按下时包含布尔值 true,没按下时包含 false
if (event.shiftKey) {
}
5、相关元素
对于 mouseover 和 mouseout 事件而言,还存在与事件相关的其他元素
这两个事件都涉及从一个元素边界内把光标转移到另一个元素边界之内
对 mouseover 来说,获得光标的元素是主要目标,相关元素是失去光标的元素
对 mouseout 来说刚好相反
event 的 relatedTarget 属性提供了相关元素的信息,这个属性只有在这两个事件上才包含值,默认为 null
6、鼠标按键
对于 mousedown 和 mouseup 事件来说,event 对象上有一个 button 属性,表示按下或释放的是哪个键;DOM 为 button 属性定义了三个值:0——鼠标主键、1——鼠标中键、3——鼠标副键
DOM3 标准规定:click 事件不支持触发右键点击事件,只能监听左键
7、额外事件信息
DOM2 Events 规范在 event 对象上提供了 detail 属性,给出了关于事件的更多信息
对鼠标事件来说,detail 提供了一个数值,表示在给定位置上发生了多少次单击,单击相当于在同一个像素上发生一次 mousedown 紧跟一次 mouseup,每次单击 detail 就会加一,如果 mousedown 和 mouseup 之间移动了,则 detail 会重置为 0
8、mousewheel 事件
mousewheel 事件会在用户使用鼠标滚轮时触发,这个事件会在任何元素上触发,在 IE8 中会冒泡到 document 事件,在现代浏览器中会冒泡的 window
mousewheel 事件的 event 对象包含鼠标事件的所有标准信息,还有一个名为 wheelDelta 的新属性;鼠标滚轮向前滚动时,wheelDelta 每次都是加 120;鼠标滚轮向后滚动时,wheelDelta 每次都是减 120
可以为任何元素或文档添加 onmousewheel 事件处理程序
9、触摸屏设备
IOS 或 Android 等触摸设备大相径庭,但是要记住:
不支持 dblclick 事件,双击窗口可以放大,但是无法覆盖这个行为
单指点击屏幕上的可点击元素会触发 mousemove 事件;如果操作会导致内容变化,则不会触发其他事件;如果屏幕上没有变化,则会相继触发 mousedown、mouseup 和 click 事件;可点击元素表示有默认动作(如链接)或指定了 onclick 事件处理程序的元素
mousemove 事件也会触发 mouseover 和 mouseout 事件
双指点击并滑动导致页面滚动时会触发 mousewheel 和 scroll 事件
移动端中不建议用 mouse 事件,而是 touchstart、touchmove、touchend
10、无障碍问题
对于残障人士,Web 应用必须小心使用事件
例如 click 事件能够通过回车键触发,其他鼠标事件则不能通过键盘触发,所以请在设计时考虑如下使用:
使用 onclick 事件处理代码,有人认为 mousedown 事件处理得更快,但是对屏幕阅读器来说这样会导致代码无法运行,因为屏幕阅读器无法触发 mousedown 事件
不要使用 mouseover 向用户显式新选项,原因同上
不要使用 dblclick 执行重要操作,键盘不能触发这个事件
键盘与输入事件
键盘事件包含三个事件:
keydown,用户按下键盘某键时触发,持续按住会重复触发
keypress,用户按下键盘上某个键并产生字符时触发,持续按住会重新触发;Esc 键也会触发这个事件;DOM3 Events 废弃了这个事件,推荐 textInput 事件
keyup,用户释放键盘上某个键时触发
所有元素都支持这些事件
输入事件只有一个,即 textInput,这个事件是对 keypress 事件的扩展,用于文本显示给用户之前更方便截获文本输入,会在文本被插入到文本框之前触发
当用户按下某个字符键时,事件触发顺序:keydown - keypress - 文本展示出来 - keyup
当用户按下非字符键时,事件触发顺序:keydown - keyup
键盘事件支持鼠标事件的修饰键,shiftKey、ctrlKey、altKey、metaKey 在键盘事件中都是可以用的
keydown 和 keypress 区别:
keydown 没有 charCode,keypress 有 charCode
keydown 可以检测到所有按键(除 fn 以外),keypress 只能监控到字符类按键
如果想检测字符以及大小写则用 keypress 更好,如果想检测控制类按键则 keydown 更好
SASCII 码转换成字母:String.fromCharCode()
1、键码
对于 keydown 和 keyup 事件,event 对象的 keyCode 属性中会保存一个键码,对应键盘上特定的一个键
对于字母和数字键,keyCode 的值与小写字母和数字的 ASCII 码一致
非字符键键码,参见红宝书 p519
2、字符编码
在 keypress 事件发生时,意味着案件会影响屏幕上显式地文本;对插入或移除字符的键,所有浏览器都会触发 keypress 事件,其他键则取决于浏览器
浏览器在 event 对象上支持 charCode 属性,只有 keypress 事件时这个属性才会被设置值,包含的是案件字符对应的 ASCII 码;其他事件中 charCode 属性值是 0,在 keypress 事件发生时则是对应的案件的键码;IE8 及更早版本浏览器和 Opera 使用 keyCode 传达字符的 ASCII 码
3、DOM3 的变化
DOM3 定义了 key、char、location、getModifierState()
但是都不尽如人愿,各个浏览器实现不同
4、textInput 事件
DOM3 Events 规范增加了 textInput 事件,在字符被输入到可编辑区域时触发;
keypress 能在任何可获得焦点的元素上触发,textInput 只能在可编辑区域上触发;keypress 对任何影响文本的键都会触发(包括退格键),textInput 只能在新字符被插入时才会触发
因为 textInput 事件主要关注字符,所以在 event 对象上提供了一个 data 属性,包含要插入的字符(不是编码),data 的值始终是要被插入的字符,而且区分大小写
event 对象上还有一个 inputMethod 属性,表示向控件中输入文本的手段,可能的值:
0,浏览器不能确定什么手段
1,键盘
2,粘贴
3,拖放
4,IME
5,单选项
6,手写
7,语音
8,组合方式
9,脚本
5、设备上的键盘事件
任天堂 Wii 遥控器上的特殊键码
红宝书 p522
合成事件
合成事件是 DOM3 Events 新增,处理通常用 IME 输入时的复杂输入序列
IME 可以让用户输入物理键盘上没有的字符,比如用键盘输入中文
合成事件有三种:
compositionstart,在 IME 的文本合成系统打开时触发,表示输入即将开始
compositionupdate,在新字符插入输入字段时触发
compositionend,在 IME 文本合成系统关闭时触发,表示恢复正常的键盘输入
在合成事件触发时,事件目标是接收文本的输入字段,唯一增加的属性是 data,其值视情况而定:
在 compositionstart 中,包含正在编辑的文本
在 compositionupdate 中,包含要插入的字符
在 compositionend 中,包含本次合成过程中输入的全部内容
变化事件
已经被 MutationObserver 代替,原有事件已经被废弃
HTML5 事件
1、contextmenu 事件
显式上下文菜单,在 windows 上是鼠标右键,在 mac 上是 ctrl+单击
contextmenu 事件冒泡,只要给 document 指定一个事件处理程序就可以处理页面上所有同类事件
事件的目标是触发操作的元素;这个事件在所有浏览器中都可以取消,在 DOM 合规的浏览器中使用 event.preventDefault(),在 IE 及更早版本中将 event.returnValue 设置为 false
通常自定义菜单都是通过这个事件触发,然后通过 onclick 事件处理程序触发隐藏
2、beforeunload 事件
该事件会在 window 上触发,用意是给开发者提供阻止页面被卸载的机会,这个事件会在页面即将从浏览器卸载时触发;这个事件不能被取消,而且会在页面上显式确认框,用户可以选择继续或退出
提示框中提示信息可以用户自定义,在 event.returnValue 属性中添加 message(IE 和 Firefox),或在事件函数中作为函数值返回(Safari 和 Chrome)
3、DOMContentLoaded 事件
该事件会在 DOM 树构建完成后立即触发,而不用等待图片资源、JavaScript 文件、css 文件或其他资源加载完成
要处理 DOMContentLoaded 事件,需要给 window 或 document 添加事件处理程序(实际事件的目标是 document,但会冒泡的 window)
该事件的 event 对象中不包含任何额外信息(除了 target 等于 document),该事件通常用于添加事件处理程序或执行其他 DOM 操作,这个事件始终在 load 之前触发
对于不支持该事件的函数,可以用超时为 0 的 setTimeout()函数,通过其回调来设置事件处理函数
不同浏览器或同一页面不同脚本的 DOMContentLoaded 事件触发时机一致并不能有绝对把握;为了尽可能早一些执行,最好是页面上第一个超时代码,但是考虑到各种因素影响,也不一定能在 load 事件之前执行超时回调
4、readystatechange 事件
支持 readystatechange 事件的每个对象都有一个 readyState 属性,该属性有下列可能的字符串值:
uninitialized,对象存在并尚未初始化
loading,对象正在加载数据
loaded,对象已加载完数据
interactive,对象可以交互,但加载尚未完成
complete,对象加载完成
在 document 上使用时,值为 interactive 的 readyState 首先会触发 readystatechange 事件,时机类似于 DOMContentLoaded,此时 DOM 树已经构建完成,可以交互了
但是对于不同页面,他和 load 事件触发时间是不能确定的
5、pageshow 与 pagehide 事件
Firefox 和 Opera 开发了一个名为往返缓存(bfcache)的功能,这个功能旨在浏览器前进或后退时加快页面之间的切换
这个缓存不仅缓存页面数据,也储存 DOM 和 javaScript 状态,实际上就是把整个页面都保存在内存里,这样一来,页面在往返切换时就不会触发 load 事件了
所以 pageshow 事件会在页面显示时触发,无论是否来自往返缓存;新加载的页面上,pageshow 会在 load 之后触发;来自往返缓存的页面,pageshow 会在页面状态完全恢复后触发;这个事件目标虽然是 document,但是处理程序必须添加到 window 上;pageshow 的 event 中包含一个 persisted 属性,true 就代表页面来自往返缓存,否则就是 false
pagehide 事件与 pageshow 事件相反,会在 unload 之前触发;pagehide 事件的 event.persisted 为 true 表示页面要进入往返缓存,否则为 false
**注意:**注册了 unload 事件处理程序(即使是空函数)的页面会自动排除在往返缓存外,当页面显式时也不会触发 load 事件,所以要注意
6、haschange 事件
HTML5 增加了 hashchange 事件,用于在 URL 散列值发生变化时通信开发者;该事件必须添加给 window,event 对象有两个属性:oldURL 和 newURL(两个变化前后完整的 url)
设备事件
随着手机和平板的流行,有一批新的浏览器交互事件随之而生
1、orientationchange 事件
Safari 浏览器上实现了该事件,用来判断用户设备是否处于水平模式还是垂直模式,window.orientation 储存了用户设备状态,它有三种值:0 - 表示垂直模式,90 - 表示左转水平模式,-90 - 表示右转水平模式,180 - 设备倒置
该事件的 event 对象没有任何有用的信息,相关信息需要从 window.orientation 上获取
IOS 设备都支持这个事件和相关属性
也可以通过向 body 元素添加 onorientationchange 属性指定事件处理程序
2、deviceorientation 事件
这个事件是 DeviceOrientationEvent 规范定义的事件,如果可以获得设备的加速器信息,而且数据发生了变化,这个事件就会在 window 上触发;该事件只反映设备在空间中的朝向
设备本身处于 3D 空间,即拥有 x、y、z 轴,如果把设备静止放在水平面上,三轴的值都为 0
三个轴相对设备是静止的

当该事件触发时,event 对象中包含各个轴相对于设备静止时的坐标值变化:
alpha:0-360 范围内的浮点值,表示围绕 z 轴旋转时 y 轴的度数(左右转)
beta:-180-180 范围内的浮点值,表示围绕 x 轴旋转时 z 轴的度数(前后转)
gamma:-90-90 范围内的浮点值,表示围绕 y 轴旋转时 z 轴的度数(扭转)
absolute:布尔值,表示设备是否返回绝对值
compassCalibrated:布尔值,表示设备的指南针是否校准正确

3、devicemotion 事件
DeviceOrientationEvent 规范定义了 devicemotion 事件;该事件用于提示设备实际上在移动,而不是仅仅改变了方向
当该事件触发时,event 对象包含以下属性:
acceleration:对象,包含 x、y、z 属性,反映不考虑重力环境下各个维度加速度信息
accelerationIncludingGravity:对象,包含 x、y、z 三个属性,反映各个维度的加速度信息,包含 z 轴自然重力加速度
interval:毫秒,距离下次触发该事件的事件
rotationRate:对象,包含 alpha,beta,gamma 属性,表示设备朝向
触摸及手势事件
1、触摸事件
手指在屏幕上滑动或从屏幕移开时,触摸事件即会触发:
touchstart:新的手指放到屏幕上触发(即使原来已经有一个手指了)
touchmove:手指在屏幕上滑动时连续触发,在这个事件中调用 preventDefault()可以阻止滚动
touchend:手指从屏幕上移开时触发
touchcancel:系统停止跟踪触摸是触发
这些事件都会冒泡,也都可以被取消,尽管触摸事件不属于 DOM 规范,但是浏览器都实现了他们
每个触摸事件的 event 对象都提供了鼠标事件的公共属性:bubbles、cancelable、view......
触摸事件还提供了三个属性用于跟踪触点:
touches:Touch 对象数组,表示当前屏幕上每个触点
targetTouches:Touch 对象的数组,表示特定于事件目标的触点
changedTouches:Touch 对象的数组,表示自上次用户动作之后变化的触点
每个 Touch 对象都包含下列属性:
clientX、clientY:触点在视口中的 x、y 坐标
identifier:触点 ID
pageX、pageY:触点在页面上 x、y 坐标
screenX、screenY:触点在屏幕上 x、y 坐标
target:触摸事件的目标
当 touchend 触发时,touches 集合中什么也没有了,因为没有滚动的触点了,需要到 event 里的 changeTouches 集合里获取
当手指触碰屏幕上的元素时,会依次触发以下事件(包括鼠标事件):touchstart - mouseover - mousemove(1 次)- mousedown - mouseup - click - touchend
2、手势事件
Safari 增加了一种手势事件,该事件会在两个手指触碰屏幕且相对距离或旋转角度变化时触发
gesturestart:第二个手指放到屏幕上时触发
gesturechange:任何一个手指在屏幕上位置发生变化时触发
gestureend:其中一个手指移开屏幕是触发
一个元素上添加手势事件处理程序,则需要两根手指都处于该元素边框内部才会触发,该事件会冒泡,所以可以在文档上添加事件
手势事件也会触发触摸事件,第二根手指触摸屏幕时,gesturestart 会先触发,然后再触发 touchstart 事件,两个手指中的一个移动就会触发 gesturechange 事件,其中一个手指离开屏幕就会触发 gestureend 事件,然后触发 touchend 事件
每个手势事件的 event 都包含所有鼠标事件属性:bubbles、cancelable、view......
新增的两个属性为:rotation 和 scale;rotation 表示手指旋转的度数,scale 表示两指间距离的变化程度,开始为 1,然后随着距离的变化相应的增大或缩小
触摸事件也会返回 rotation 和 scale,但只在两个手指触碰屏幕时才会变化
事件参考
DOM 和 HTML5 规范,以及概述事件行为的其他当前已发布规范中定义的所有浏览器事件
红宝书 p534
拖拽事件
function drag(elem) {
var disX, disY;
elem.addEventListener(
"mousedown",
function (e) {
disX = e.pageX - elem.pageX;
disY = e.pageY - elem.pageY;
document.addEventListener(
"mousemove",
function (e) {
var event = e || window.event;
div.style.left = -disX + event.pageX + "px";
div.style.top = -disY + event.pageY + "px";
},
false
);
},
false
);
elem.addEventListener(
"mouseup",
function (e) {
document.addEventListener("mousemove", null, false);
},
false
);
} // 使用封装方法function drag(elem) { var disX, disY; addEvent(elem, 'mousedown', function(e) { var event = e || window.event; disX = event.clientX - parseInt(getStyle(elem, 'left')); disY = event.clientY - parseInt(getStyle(elem, 'top')); addEvent(document, 'mousemove', mouseMove); addEvent(elem, 'mouseup', mouseUp); stopBubble(event); cancelHandler(event); }); function mouseMove(e) { var event = e || window.event; elem.style.left = -disX + event.pageX + "px"; elem.style.top = -disY + event.pageY + "px"; } function mouseUp(e) { var event = e || window.event; removeEvent(document, 'mousemove', mouseMove); removeEvent(elem, 'mouseup', mouseUp); }}
文本类操作事件
input.oninput
input 内容改变就会触发
input.onchange
input 聚焦和试去焦点时内容是否改变,改变就触发
窗体操作事件
window.onscroll()
页面滑动触发
window.onload()
方法最慢,不建议用
在整个页面全部加载完后才会触发、
内存与性能
在 js 中,页面中事件处理程序的数量与页面整体性能直接相关
首先,每个函数都是对象,都占用内存空间,对象越多,性能越差;其次,为指定事件处理程序所需访问的 DOM 的次数会先期造成整个页面交互的延迟;需要在使用事件处理程序时注意一些方法,就可以改善页面性能
事件委托
事件委托利用冒泡,可以只用一个事件处理程序来管理一种类型的事件
应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件,该方法有如下有点:
document 对象随时可用,任何时候都能给他添加事件处理程序(不用等待 DOMContentLoaded 事件或 load 事件)
节省花在页面事件处理程序上的时间,只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间
减少整个页面所需内存
删除事件处理程序
删除绑定了事件处理程序的元素,第一个方法是 removeChild()和 replaceChild()删除;第二种是通过 innerHTML 方法替换页面某个部分,这时候如果该部分有事件处理程序,就不会被垃圾回收程序回收,所以要及时删除事件处理程序。再者,在事件处理程序中删除该元素会阻止事件冒泡,只有事件目标仍位于文档中事件才会冒泡
页面卸载残留的事件处理程序,如果在页面卸载后事件处理程序没有被清理,他们就会残留在内存中,之后每次加载页面内存中残留就会增加;最好在 onunload 事件中趁页面尚未卸载先删除所有事件处理程序(这时也体现了事件委托的优势)
模拟事件
DOM 事件模拟
可以使用 document.createEvent()方法创建一个 event 对象,这个方法接收一个参数,此参数表示要创建事件类型的字符串。在 DOM2 中,这些字符串都是复数形式,DOM3 中它们又变成单数形式了,有以下值:
UIEvents(DOM3 是 UIEvent):通用用户界面事件(鼠标和键盘事件都继承自它)
MouseEvents(DOM3 是 MouseEvent):通用鼠标事件
HTMLEvents(DOM3 没有):通用 HTML 事件
键盘事件是在 DOM3 Events 中增加的
创建好事件后,需要使用 dispatchEvent()方法,这个方法在所有支持事件的 DOM 上都存在,该方法接收一个 event 对象参数,然后事件就会冒泡触发事件处理程序执行
1、模拟鼠标事件
创建鼠标 event 对象后(传入 MouseEvents),可以用 initMouseEvent()方法,用于为新对象指定鼠标的特定信息该方法接收 15 个参数:
type:要触发的事件类型,如“click”
bubbles:表示时间是否冒泡,为精确模拟鼠标事件,应该设置为 true
cancelable:表示事件是否可以取消,为精确模拟鼠标事件,应该设置为 true
view:与事件关联的视图,基本上始终是 document.defaultView
detail:关于事件的额外信息,制备书记兼处理程序使用,通常为 0
screenX:事件相对于屏幕的 x 轴的坐标
screenY:事件相对于屏幕的 y 轴的坐标
clientX:事件相对于视口的 x 轴的坐标
clientY:事件相对于视口的 y 轴的坐标
ctrlKey:表示是否按下了 Ctrl 键,默认为 false
altKey:表示是否按下了 Alt 键,默认为 false
shiftKey:表示是否按下了 Shift 键,默认为 false
metaKey:表示是否按下了 Meta 键,默认为 false
button:表示暗下了那个按钮,默认为 0
relatedTarget:与事件相关的对象,只在模拟 mouseover 和 mouseout 时使用
前四个参数是重要参数,因为这是浏览器需要用到的,event 对象的 target 属性会自动设置为调用 dispatchEvent()方法时传入的节点
2、模拟键盘事件
DOM2 Events 中没有定义键盘事件,在 DOM3 中创建键盘事件(传入 KeyboardEvent),其返回的 event 对象也有一个 initKeyboardEvent()方法,接收以下参数:
type:要触发的事件类型,如“keydown”
bubbles:表示事件是否冒泡,为精确模拟键盘事件,应该设置为 true
cancelable:表示事件是否可以取消,为精确模拟键盘事件,应该设置为 true
view:与事件关联的视图,基本上始终是 document.defaultView
key:按下按键的字符串代码
location:按下按键的位置,0 表示默认键、1 表示左边、2 表示右边、3 表示数字键盘、4 表示移动设备、5 表示游戏手柄
modifiers:空格分隔的修饰键列表,如“shift”
repeat:连续按了这个键多少次
DOM3 Events 废弃了 keypress 事件,因此只能通过上述方式模拟 keydown 和 keyup
最好检测以下浏览器对 DOM3 的支持情况,其他浏览器会返回非标准的 KeyboardEvent 对象
Firefox 允许传入“KeyEvents”来创建键盘事件,返回的对象包含 initKeyEvent()方法,包含以下参数:
type:要触发的事件类型,如“keydown”
bubbles:表示事件是否冒泡,为精确模拟键盘事件,默认为 true
cancelable:表示事件是否可以取消,为模拟键盘事件,默认为 true
view:与事件关联的视图,基本上始终是 document.defaultView
ctrlkey:表示是否按下了 ctrl 键,默认为 false
altkey、shiftkey、metakey
keyCode:表示按下或释放键的键码,keydown 和 keyup 中使用,默认为 0
charCode:按下键对应字符的 ASCII 编码,在 keypress,默认为 0
通过调用 dispatchEvent()并传入 event 对象来触发
对于其他浏览器,则需要传入“Events”来创建通用事件
3、模拟其他事件
模拟 HTML 事件(传入 HTMLEvents),使用 initEvent()方法初始化
HTML 事件在浏览器中很少用,因为它们用处有限
4、自定义 DOM 事件
调用 createEvent("CustomEvent")返回的对象包含 initCustomEvent()方法,接收以下 4 个参数:
type:要触发的事件类型,如“myevent”
bubbles:表示事件是否冒泡
cancelable:表示事件是否可以取消
detail:任意值,作为 event 对象的 detail 属性
IE 事件模拟
IE 相关,懂得都懂,红宝书 p547