? ? ? ? JavaScript与HTML交互是通过事件实现的,而事件流是为了页面接收事件的顺序。存在两种事件流方案:事件冒泡流 和 事件捕获流,IE支持事件冒泡流,Netscape Communicator支持事件捕获流。
? ? ? ? 事件冒泡流被定义从最具体的元素(文档树中最深节点)开始触发,然后向上传播至没有那么具体的元素(文档)。示例顺序:div -> body > html -> document -> window
? ? ? ? 事件捕获流是最不具体的节点最先接收事件,最具体的节点应该最后收到事件(事件捕获是为了在事件抵达最终目标前拦截事件)。示例顺序:window -> document -> html -> body -> div
? ? ? ? DOM2 Events规范规定事件流分3个阶段:事件捕获->目标元素接收事件->事件冒泡,事件捕获为拦截事件提供可能。示例顺序:document -> html -> body -> div -> body -> html -> document
规范规定捕获阶段不能命中事件目标,但现代浏览器在捕获阶段在事件目标上触发事件,也即有两个机会处理事件。
? ? ? ? 事件是用户或浏览器执行的动作,如:单击、加载、鼠标悬停,为响应事件调用的函数被称为事件处理程序(或事件监听器),事件处理程序名字以‘on’开头,onclick、onload等。
? ? ? ? 将事件处理程序名字作为HTML的属性来指定事件,属性值是JavaScript代码字符串。以这种方式指定事件处理程序,会创建一个函数Fn封装属性的值(即JavaScript代码字符串),Fn有特殊的局部变量event,并且Fn中this指向目标元素。并且Fn中可直接访问document和元素的成员。
<input onclick="console.log(event.type)"/>
<input onclick="console.log(this.value)"/>
<input onclick="console.log(value)"/>
Fn的实现类似这样:
function Fn(event) {
with(document) {
with(this) {
// 属性值
}
?????}
}
? ? ? ? 将一个函数赋值给DOM元素的一个事件处理程序属性。此函数内this绑定当前DOM元素。
let btn= document.getElementById('btn');
btn.onclick = function(event) {
console.log(this.id) // 'btn'
}
// 仅支持绑定一个事件处理程序
// 移除事件处理程序
btn.onclick = null;
? ? ? ? DOM2 events添加了addEventListener和removeListener,此两个方法接收三个参数:事件名、事件处理函数和一个布尔值,true表示捕获阶段调用事件处理程序,false(默认)表示冒泡阶段调用事件处理程序。
let btn = document.getElementById('btn');
btn.addListener('click', () => {
console.log(this.id);
}, false);
// 事件处理程序同样在被附加到的元素的作用域中运行,即this指向元素本身。
// 且支持多次绑定,会按顺序执行
let btn = document.getElementById('btn');
btn.addListener('click', (event) => {
console.log('Hello World!');
}, false);
? ? ? ? IE实现attachEvent和detachEvent方法,此两个方法接收两个参数:事件名和事件处理函数(没有第3个参数,即在冒泡阶段调用事件处理程序,且事件名称需要前缀'on')。
let btn = document.getElementById('btn');
btn.attachEvent('onclick', function() {
console.log(this === window); // true
});
// this指向window对象
// 也支持绑定多个事件处理程序,按相反顺序执行,后绑定的先执行。
btn.attachEvent('onclick', function() {
console.log('Hello World!');
});
? ? ? ? DOM发生事件时所有相关信息都会被收集并存储在一个名为event的对象中。(注:标灰色的有点出入,不是挂在Event原型对象下的)
Event原型对象的属性和方法 (只读) | 说明 | 示例 |
Event.prototype.type | 功能:获取被触发的事件类型 输出:string | event.type 取值范围:'click' |'mouseover' | 'mouseout' | ... |
Event.prototype.target | 功能:获取事件目标 输出:Element | event.target |
Event.prototype.currentTarget | 功能:当前事件处理程序所在的元素 输出:Element | event.currentTarget |
Event.prototype.bubbles | 功能:表示事件是否冒泡 输出:boolean | event.bubbles 得到true |
Event.prototype.cancelable | 功能:表示是否可以取消事件的默认行为 输出:boolean | event.cancelable 得到true |
Event.prototype.eventPhase | 功能:表示调用事件处理程序的阶段,1代表捕获阶段,2代表到达目标,3代表冒泡阶段 | event.eventPhase |
Event.prototype.detail | 功能:事件相关其他信息 输出:number | event.detail |
Event.prototype.trusted | 功能:true表示事件是由浏览器生成的,false表示开发者通过JS创建的 输出:boolean | event.trusted |
Event.prototype.View | 功能:与事件相关的抽象视图,等于事件所发生的window对象 输出:AbstractView | event.View |
Event.prototype.defaultPrevented | 功能:true表示已经调用preventDefault()方法 输出:boolean | event.defaultPrevented |
Event.prototype.preventDefault() | 功能:取消事件的默认行为,仅cancellable为true才可以调用该方法 输入:无 输出:无 | event.preventDefault() |
Event.prototype.stopPropagation() | 功能:取消所有后续“事件捕获”和“事件冒泡”,仅bubbles为true才可以调用该方法 输入:无 输出:无 | event.stopPropagation() |
Event.prototype .stopImmediatePropagation() | 功能:取消所有后续“事件捕获”和“事件冒泡”,并阻止调用任何后续事件处理程序 输入:无 输出:无 | event .stopImmediatePropagation() |
IE事件对象 | ||
Event.prototype.cancelBubble (读写) | 功能:默认false,设置true可以取消冒泡,与stopPropagation()方法相同 | event.cancelBubble = true |
Event.prototype.returnValue (读写) | 功能:默认ture,设置false可以取消事件默认行为,与preventDefault()方法相同 | event.returnValue = false; |
Event.prototype.srcElement | 功能:事件目标,与target属性相同 | event.srcElement |
Event.prototype.type | 功能:触发的事件类型 | event.type |
/*
this和currentTarget都等于document.body,因为它是注册事件处理程序的元素
target等于按钮本身,它是click事件真正的目标。但它没有事件处理程序,因此冒泡到document.body
*/
document.body.onclick = funtion(event) {
console.log(event.currentTarget === document.body); // true
console.log(this === document.body); // true
console.log(event.target === document.getElementById('btn')); // true
}
// event.type
let btn = document.getElementById('btn');
btn.onclick = function(event) {
console.log(event.eventPhase); // 2
}
document.body.addEventListener('click', (event) => {
console.log(event.eventPhase); // 1
})
document.body.onclick = (event) => {
cnosole.log(event.eventPhase); // 3
}
事件类型 (DOM3 Events) | 说明 |
UIEvent | UIEvent -> Event 用户界面事件,涉及与BOM交互的通用浏览器事件 |
load:window上当页面加载完成后触发,在frameset上当所有frame都加载完成后触发,img上图片加载完触发,object上当相应对象加载完成后触发(window.onload、<body loload="console.log('Loaded')"/>) unload:window上当页面完全卸载后触发,在frameset上当所有frame都卸载完成后触发,object上当相应对象卸载完成后触发 abort:object上在相应对象加载完成前,被用户提前终止下载时触发 error:window上JS报错时触发,在frameset上当一个或多个frame无法完成加载时触发,img无法加载指定图片时触发,object无法加载相应对象时触发 select:文本框select或textarea上用户选择一个或多个字符时触发 resize:window或frame被缩放时触发 scroll:用户滚动包含滚动条的元素时,在元素上触发 归为HTMLEvents UIEvent.prototype.detail表示在给定位置单击多少次,如果mousedown和mouseup之间移动,detail重置为0 | |
FocusEvent | FocusEvent -> UIEvent 焦点事件,在元素获取和失去焦点时触发 |
blur:元素失去焦点时触发,不会冒泡 focusout:元素失去焦点时触发,是blur的通用版 focus:元素获得焦点时触发,不会冒泡 focusin:元素获得焦点时触发,是focus的冒泡版 | |
MouseEvent | MouseEvent -> UIEvent -> Event 鼠标事件,使用鼠标在页面执行某些操作时触发?DOM3 Events |
click:用户单击鼠标主键(通常是左键)或按键盘回车时触发(同个元素mousedown->mouseup) dbclick:用户双击鼠标主键(通常是左键)时触发。(两次click) mouseenter:用户把鼠标光标从元素外部移到元素内部时触发(不冒泡,也不会在光标经过后代元素时触发) mousedown:用户按下任意鼠标键触发(不能通过键盘触发) mouseup:用户释放鼠标键触发(不能通过键盘触发) mousemove:鼠标光标在元素上移动时,反复触发(不能通过键盘触发) mouseover:用户把鼠标光标从元素外部移到元素内部时触发(不能通过键盘触发)——事件的主要目标是获得光标的元素 mouseout:用户把鼠标光标从一个元素移到另一个元素上时触发(移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素,不能通过键盘触发)——事件主要目标是失去光标的元素 示例:比如从div移到body,div上会触发mouseout,关联元素body;body上触发mouseover,关联元素div,事件对象关联属性:event.relatedTarget mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dbclick event.clientX, event.clientY相对浏览器内容窗口左上角的坐标(不考虑页面滚动) event.pageX, event.pageY相对于页面左上角上坐标 event.screenX, event.screenY相对于屏幕左上角坐标 4个修饰键:shiftKey ctrlKey altKey metaKey 鼠标按键:button 0表示鼠标主键、1表示鼠标中键(通常为滚轮)、2表示鼠标副键 | |
WheelEvent | WheelEvent -> MouseEvent -> UIEvent -> Event 滚轮事件,使用鼠标滚轮(或类似设备)时出发 |
mousewheel:鼠标滚轮滚动时触发 WheelEvent.prototype.wheelDelta | |
InputEvent | InputEvent -> UIEvent -> Event 输入事件,向文档中输入文本时出发 |
textInput: | |
KeyboardEvent | KeyboardEvent -> UIEvent -> Event 键盘事件,使用键盘在页面上执行某些操作时触发 |
keydown:用户按下键盘上某个键时触发,持续按住会重复触发 keypress:用户按下键盘上某个键并产生字符时触发,持续按住会重复触发。Esc键也会触发(DOM3 Events推textInput事件) keyup:用户释放键盘上某个键时触发 4个修饰键:shiftKey ctrlKey altKey metaKey 键码:keyCode | |
CompositionEvent | CompositionEvent -> UIEvent -> Event 合成事件,在使用某种IME(输入法编辑器)输入字符时触发 |
compositionstart:在 IME 的文本合成系统打开时触发,表示输入即将开始 compositionupdate:在新字符插入输入字段时触发 compositionend:在 IME 的文本合成系统关闭时触发,表示恢复正常键盘输入 | |
HTML5事件 | |
contextmenu | 专门用于表示何时该显示上下文菜单,从而允许开发者取消默认的上下文菜单并提供自定义菜单 |
beforeunload | 用意是给开发者提供阻止页面被卸载的机会 |
DOMContentLoaded | 在 DOM 树构建完成后立即触发,而不用等待图片、JS文件、CSS 文件或其他资源加载完成 |
readystatechange | 旨在提供文档或元素加载状态的信息,但行为有时候并不稳定 |
pageshow,pagehide | 旨在使用浏览器“前进”和“后退”按钮时加快页面之间的切换。 |
hashchange | 用于在 URL 散列值(URL 最后 # 后面的部分)发生变化时通知开发者 |
设备事件 | |
orientationchange | 方便开发者判断用户的设备是处于垂直模式还是水平模式 |
deviceorientation | 如果可以获取设备的加速计信息,而且数据发生了变化,这个事件就会在 window 上触发 |
devicemotion | 这个事件用于提示设备实际上在移动,而不仅仅是改变了朝向 |
触摸及手势事件 | |
touchstart | 手指放到屏幕上时触发(即使有一个手指已经放在了屏幕上) |
touchmove | 手指在屏幕上滑动时连续触发。在这个事件中调用 preventDefault() 可以阻止滚动 |
touchend | 手指从屏幕上移开时触发 |
touchcancel | 系统停止跟踪触摸时触发。文档中并未明确什么情况下停止跟踪 |
gesturestart | 一个手指已经放在屏幕上,再把另一个手指放到屏幕上时触发 |
gesturechange | 任何一个手指在屏幕上的位置发生变化时触发 |
gestureend | 其中一个手指离开屏幕时触发 |
还有其他很多类事件,可以参考一些专业文档。
? ? ? ? 页面中事件处理程序的数量和页面整体性能直接相关,一是函数都是对象,占用内存空间,对象越多,性能越差;二是指定事件处理程序所需访问DOM次数会先期造成整个页面交互的延迟。
? ? ? ? 比如可以在整个指定一个onclick事件处理程序,而不给每个元素指定事件处理程序。适合事件委托的事件包括:click、mousedown、mouseup、keydown和keypress,不适合事件委托:mouseover和mouseout,不好处理。
<!DOCTYPE html>
<html>
<head>
<title>事件委托</title>
</head>
<body>
<ul id='myLinks'>
<li id='goSomeWhere'>Go somewhere</li>
<li id='doSomething'>Do something</li>
<li id='sayHi'>Say hi</li>
</ul>
<script>
let list = document.getElementById('myLinks');
list.addEventListener('click', (event) => {
let target = event.target;
switch(target.id) {
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http:// www.wrox.com";
break;
case "sayHi":
console.log("hi");
break;
}
})
</script>
</body>
</html>
????????把事件处理程序指定给元素后,在浏览器代码和负责页面交互的 JavaScript 代码之间就建立了联系,联系建立越多,页面性能越差,因为需要及时删除不用的事件处理程序。
<!DOCTYPE html>
<html>
<head>
<title>事件委托</title>
</head>
<body>
<div id="myDiv">
<input type="button" value="Click Me" id="myBtn">
</div>
<script>
let btn = document.getElementById("myBtn");
btn.onclick = function() {
// 执行操作
btn.onclick = null; // 删除事件处理程序
document.getElementById("myDiv").innerHTML = "Processing...";
};
</script>
</body>
</html>
? ? ? ? 事件doucment.createEvent方法创建一个event对象。
let btn = document.getElementById("myBtn");
// 创建 event 对象
let event = document.createEvent("MouseEvents");
// 初始化 event 对象
event.initMouseEvent("click", true, true, document.defaultView,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
// 触发事件
btn.dispatchEvent(event);
注:以上,如有不合理之处,还请帮忙指出,大家一起交流学习~