《JavaScript高级程序设计》读书笔记
事件流
描述了页面接收事件的顺序。事件捕获——目标阶段——事件冒泡
事件处理程序
HTML事件处理程序
<form method="post">
<input type="text" name="userName" value="" />
<input type="button" value="输出" onclick="console.log(userName.value)"/>
</form>
<!--
事件处理程序对应的函数等价于:
function() {
with(document) {
with(this.form) {
with(this) {
// 属性值
}
}
}
}
-->
DOM0 事件处理程序
一个函数赋值给(DOM元素的)一个事件处理程序属性。
let btn = document.getElementById('myBtn');
btn.onclick = funciton() {
console.log(this.id); // 'myBtn' this引用元素本身
}
btn.onclick = null; // 移除事件处理程序
DOM2 事件处理程序
- addEventListener()
- removeEventListener()
接收3个参数:事件名、事件处理函数和一个布尔值(true表示在捕获阶段调用,false默认表示在冒泡阶段调用处理程序)
let btn = document.getElementById("myBtn");
let handler = function() {
console.log(this.id);
}
btn.addEventListener("click", handler, false);
btn.removeEventListener("click",handler, false); // 移除
事件对象
所有相关信息都会被收集并存储在一个名为event 的对象中。
DOM 事件对象
event 对象是传给事件处理程序的唯一参数。
let btn = document.getElementById("myBtn");
btn.onclick = function(event) {
console.log(event.type); // "click"
};
btn.addEventListener("click", (event) => {
console.log(event.type); // "click"
}, false);
// HTML中
// <input type="button" value="Click Me" onclick="console.log(event.type)">
所有事件对象都会包含下表列出的这些公共属性和方法:
属性/方法 | 说明 |
---|---|
bubbles | 布尔值 只读 表示事件是否冒泡 |
cancelable | 布尔值 只读 表示是否可以取消事件的默认行为 |
currentTarget | 元素 只读 当前事件处理程序所在的元素(this对象始终等于这个) |
defaultPrevented | 布尔值 只读 true 表示已经调用 preventDefault()方法( DOM3 Events 中新增) |
detail | 整数 只读 事件相关的其他信息 |
eventPhase | 整数 只读 表示调用事件处理程序的阶段: 1 代表捕获阶段, 2 代表到达目标, 3 代表冒泡阶段 |
preventDefault() | 函数 只读 用于取消事件的默认行为。只有 cancelable 为 true 才可以调用这个方法 |
stopImmediatePropagation() | 函数 只读 用于取消所有后续事件捕获或事件冒泡,并阻止调用任何后续事件处理程序( DOM3 Events 中新增) |
stopPropagation() | 函数 只读 用于取消所有后续事件捕获或事件冒泡。只有 bubbles为 true 才可以调用这个方法。 |
target | 元素 只读 事件目标(如果事件处理程序直接添加在了意图的目标,则this、currentTarget 和 target 三者相同) |
trusted | 布尔值 只读 true 表示事件是由浏览器生成的。 false 表示事件是开发者通过 JavaScript 创建的( DOM3 Events 中新增) |
type | 字符串 只读 被触发的事件类型 |
View | AbstractView 只读 与事件相关的抽象视图。等于事件所发生的 window 对象 |
事件类型
- 用户界面事件( UIEvent):涉及与 BOM 交互的通用浏览器事件。
- 焦点事件( FocusEvent):在元素获得和失去焦点时触发。
- 鼠标事件( MouseEvent):使用鼠标在页面上执行某些操作时触发。
- 滚轮事件( WheelEvent):使用鼠标滚轮(或类似设备)时触发。
- 输入事件( InputEvent):向文档中输入文本时触发。
- 键盘事件( KeyboardEvent):使用键盘在页面上执行某些操作时触发。
- 合成事件( CompositionEvent):在使用某种 IME( Input Method Editor,输入法编辑器)输入字符时触发。
用户界面事件
- load:在 window 上当页面加载完成后触发,在窗套( <frameset>)上当所有窗格( <frame>)都加载完成后触发,在<img>元素上当图片加载完成后触发,在<object>元素上当相应对象加载完成后触发。
- unload:在 window 上当页面完全卸载后触发,在窗套上当所有窗格都卸载完成后触发,在<object>元素上当相应对象卸载完成后触发。
- abort:在<object>元素上当相应对象加载完成前被用户提前终止下载时触发。
- error:在 window 上当 JavaScript 报错时触发,在<img>元素上当无法加载指定图片时触发,在<object>元素上当无法加载相应对象时触发,在窗套上当一个或多个窗格无法完成加载时触发。
- select:在文本框( <input>或 textarea)上当用户选择了一个或多个字符时触发。
- resize:在 window 或窗格上当窗口或窗格被缩放时触发。
- scroll:当用户滚动包含滚动条的元素时在元素上触发。 <body>元素包含已加载页面的滚动条。大多数 HTML 事件与 window 对象和表单控件有关。
1. load 事件
js 方式:
window.addEventListener("load", (event) => {
console.log("Loaded!");
});
指定 load 事件处理程序的方式:
<body onload="console.log('Loaded!')">
图片也会出发load事件:
window.addEventListener("load", () => {
let image = document.createElement("img");
image.addEventListener("load", (event) => {
console.log(event.target.src);
});
document.body.appendChild(image);
image.src = "smile.gif";
});
2. unload 事件
一般是在从一个页面导航到另一个页面时触发,最常用于清理引用,以避免内存泄漏。
window.addEventListener("unload", (event) => {
console.log("Unloaded!");
});
<body onunload="console.log('Unloaded!')">
3. resize 事件
window.addEventListener("resize", (event) => {
console.log("Resized");
});
4. scroll 事件
window.addEventListener("scroll", (event) => {
if (document.compatMode == "CSS1Compat") {
console.log(document.documentElement.scrollTop);
} else {
console.log(document.body.scrollTop);
}
});
焦点事件
- blur:当元素失去焦点时触发。这个事件不冒泡,所有浏览器都支持。
- focus:当元素获得焦点时触发。这个事件不冒泡,所有浏览器都支持。
- focusin:当元素获得焦点时触发。这个事件是 focus 的冒泡版。
- focusout:当元素失去焦点时触发。冒泡版。
鼠标和滚轮事件
- click:在用户单击鼠标主键(通常是左键)或按键盘回车键时触发
- dblclick:在用户双击鼠标主键(通常是左键)时触发。
- mousedown:在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。
- mouseenter:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。
- mouseleave:在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在光标经过后代元素时触发。
- mousemove:在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。
- mouseout:在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发。
- mouseover:在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发。
- mouseup:在用户释放鼠标键时触发。这个事件不能通过键盘触发。
1. 客户端坐标(clientX、clientY)
事件发生时鼠标光标在视口中的坐标。
let div = document.getElementById("myDiv");
div.addEventListener("click", (event) => {
console.log(`Client coordinates: ${event.clientX}, ${event.clientY}`);
});
2. 页面坐标(pageX、pageY)
3. 屏幕坐标(screenX、screenY)
4. 修饰键
- shiftKey
- ctrlKey
- altKey
- metaKey
document.addEventListener("click", (event) => {
let keys = new Array();
if (event.shiftKey) {
keys.push("shift");
}
if (event.ctrlKey) {
keys.push("ctrl");
}
if (event.altKey) {
keys.push("alt");
}
if (event.metaKey) {
keys.push("meta");
}
console.log("Keys: " + keys.join(","));
});
5. 相关元素(relatedTarget)
只有在mouseover 和 mouseout 事件发生时才包含值。
let 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;
}
},
// 其他代码
};
let div = document.getElementById("myDiv");
div.addEventListener("mouseout", (event) => {
let target = event.target;
let relatedTarget = EventUtil.getRelatedTarget(event);
console.log(`Moused out of ${target.tagName} to ${relatedTarget.tagName}`);
});
6. 鼠标按键(button)
返回一个值,代表用户按下并触发了事件的鼠标按键。
-
0
:主按键,通常指鼠标左键或默认值 -
1
:辅助按键,通常指鼠标滚轮中键 2
:次按键,通常指鼠标右键
7. 额外事件信息(detail)
提供当前点击数
8. mousewheel 事件
在用户使用鼠标滚轮时触发。每次间距120。
document.addEventListener("mousewheel", (event) => {
console.log(event.wheelDelta);
});
9. 触摸屏设备
- 不支持 dblclick 事件。双击浏览器窗口可以放大,但没有办法覆盖这个行为。
- 单指点触屏幕上的可点击元素会触发 mousemove 事件。如果操作会导致内容变化,则不会再触发其他事件。如果屏幕上没有变化,则会相继触发 mousedown、 mouseup 和 click 事件。点触不可点击的元素不会触发事件。可点击元素是指点击时有默认动作的元素(如链接)或指定了 onclick 事件处理程序的元素。
- mousemove 事件也会触发 mouseover 和 mouseout 事件。
- 双指点触屏幕并滑动导致页面滚动时会触发 mousewheel 和 scroll 事件。
键盘与输入事件
- keydown,用户按下键盘上某个键时触发,而且持续按住会重复触发。
- keypress,用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。 Esc 键也会触发这个事件。 DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件。
- keyup,用户释放键盘上某个键时触发。
键盘事件支持与鼠标事件相同的修饰键。
1. 键码(keyCode)
键码表
2. 字符编码
只有在keypress 事件触发时,event.charCode/event.keyCode(早期)。
String.fromCharCode() 方法编码转化为字符。
3. DOM3 的变化
key:等于文本字符,在按下非字符键时为键名
textInput事件:在字符被输入到可编辑区域时触发。
document.addEventListener('textInput',e => console.log(e.data))
合成事件
compositionstart:在IME的文本合成系统打开时触发,表示输入即将开始。例如,当用户使用拼音输入法开始输入汉字时,这个事件就会被触发。
compositionupdate:在新字符插入输入阶段时触发
compositionend:在IME文本合成系统关闭时触发,表示恢复正常键盘输入
HTML5 事件
1. beforeunload 事件
在页面即将从浏览器中卸载时触发。弹出一个确认框,确认是否关闭页面。
window.addEventListener('beforeunload', (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = '';
});
2. DOMContentLoaded 事件
当初始的 HTML 文档被完全加载和解析完成之后,**DOMContentLoaded **事件被触发,而无需等待样式表、图像和子框架的完全加载。始终在load 事件之前触发。
document.addEventListener('DOMContentLoaded',function(){
console.log('3 seconds passed');
});
3. readystatechange 事件
当文档的 readyState 属性发生改变时,会触发 readystatechange 事件。
可能具有属性:
- uninitialized:对象存在并尚未初始化
- loading:对象正在加载数据
- loaded:对象已经加载完数据
- interactive:对象可以交互,但尚未加载完成
- complete:对象加载完成
5. pageshow 与 pagehide 事件
往返缓存:使用浏览器后退/前进按钮操作的缓存
paegshow:无论是否来自缓存,都会触发该事件
pagehide:在页面从浏览器卸载后触发
都带有persisted 属性,如果在往返缓存中为ture,否则为false。
6. hashchange 事件
在URL 散列值发生变化时触发
设备事件
用于确定用户使用设备的方式
1. orientationchange 事件
判断用户设备是处于垂直模式还是水平模式。0 表示垂直模式,90 表示左转水平模式,-90 表示右转水平模式。
2. deviceorientation 事件
获取移动设备方向和动作数据。主要是以下5个属性:
- alpha:0~360 范围内的浮点值,表示围绕z轴旋转时y轴的度数(左右转)
- beta:-180~180 范围内的浮点值,表示围绕x轴旋转时z轴的度数(前后转)
- gamma:-90~90 范围内的浮点值,表示围绕y轴旋转时z轴的度数(扭转)
- absolute:布尔值,表示设备是否返回绝对值
- compassCalibrated:布尔值,表示设备的指南针是否正确校准
3. devicemotion 事件
用于提示设备实际上在移动,而不仅仅时改变了朝向。例如:可以用来确认设备正在掉落或者正拿在一个行走的人手里。包含如下额外属性:
acceleration:对象,包含x、y和z 属性,反映不考虑重力情况下各个维度的加速信息。
accelerationIncludingGravity:对象,包含x、y和z 属性,反映各个维度的加速信息,包含z 轴自然重力加速度。
interval:毫秒,距离下次触发devicemotion 事件的时间。
rotationRate:对象,包含alpha、beta和gamma 属性,表示设备朝向。
触摸及手势事件
1. 触摸事件
- touchstart:手指放到屏幕上时触发
- touchmove:手指在屏幕上滑动时连续触发
- touchend:手指从屏幕上移开时触发
- touchcancel:系统停止跟踪触摸时触发
除了包含那些公共的DOM属性,还提供了以下3个属性用于跟踪触点:
- touches:Touch 对象数组,表示当前屏幕上的每个触点
- targetTouches:Touch 对象数组,表示特定于事件目标的触点
- changedTouches:Touch 对象数组,表示自上次用户动作之后变化的触点
每个Touch 对象都包含下列属性:
- clientX:触点在视口中的x 坐标
- clientY:触点在视口中的y 坐标
- identifier:触点ID
- pageX:触点在页面上的x坐标
- pageY:触点在页面上的y坐标
- screenX:触点在屏幕上的x坐标
- screenY:触点在屏幕上的y坐标
- target:触摸事件的事件目标
2. 手势事件
会在两个手指触碰屏幕且相对距离或旋转角度变化时触发。只有两个手指同时接触事件接受者时,才会触发。
- gesturestart:一个手指已经放在屏幕上,再把另一个手指放到屏幕上时触发
- gesturechange:任何一个手指在屏幕上的位置发生变化时触发
- gestureend:其中一个手指离开屏幕时触发
新增两个属性:
rotation:表示手指变化旋转的度数,负值表示逆时针旋转,正值表示顺时针
scale:表示两指之间距离变化的程度。开始值为1
内存与性能
1. 事件委托
利用事件冒泡,使用一个事件处理程序来管理一种类型的事件。
<ul id="myLinks">
<li id="a">aaa</li>
<li id="b">bbb</li>
</ul>
let item1 = document.getElementById("a")
let item2 = document.getElementById("b")
item1.addEventListener("click" ,e=>console.log(e))
item2.addEventListener("click" ,e=>console.log(e))
// 优化后
let list = document.getElementById("myLinks");
list.addEventListener('click',e=>{
let target = e.target;
switch(target.id){
case 'a':
console.log(1);
break;
case 'b':
console.log(2);
break;
}
})
2. 删除事件处理程序
即使删除不用的事件处理程序。
模拟事件
document.createEvent() 方法 创建一个event对象,接受一个参数:
- UIEvents:通过用户界面事件
- MouseEvents:通过鼠标事件
- HTMLEvents:通过HTML事件
dispatchEvent() 方法触发事件
1. 模拟鼠标事件
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);
createEvent() 返回一个event 对象,该对象有一个initMouseEvent() 方法,接受15个参数:
- type:要触发的事件类型
- bubbles:表示事件是否冒泡。
- cancelable(布尔值):表示事件是否可以取消
- view:与事件关联的视图
- detail(整数):关于事件的额外信息。
- screenX(整数):事件相对于屏幕的x 坐标
- screenY:事件相对于屏幕的y 坐标
- clientX:事件相对与视口的x坐标
- clientY:事件相对于视口的y坐标
- ctrlkey(布尔值):表示是否按下Ctrl键,默认false
- altkey(布尔值):是否按下Alt键
- shiftkey
- metakey
- button(整数):表示按下了那个按钮。默认0
- relatedTarget(对象):与事件相关的对象
2. 模拟键盘事件
传入参数:KeyboardEvent
initKeyboardEvent() 方法参数:
- type
- bubbles
- cancelable
- view
- key(字符串):按下按键的字符串代码
- location(整数):按下按键的位置
- modifiers(字符串):空格分隔的修饰键列表
- repeat(整数):连续按了这个键多少次
3. 模拟其他事件
let event = document.createEvent("HTMLEvents");
event.initEvent("focus", true, false);
target.dispatchEvent(event);
4. 自定义DOM 事件
自定义事件不会触发原生DOM 事件,但可以让开发者定义自己的事件。
4个参数:
- type
- bubbles
- cancelable
- detail
let div = document.getElementById("myDiv"), event;
div.addEventListener("myevent", event =>{
console.log('my div' + event.detail);
});
document.addEventListener("myevent", event =>{
console.log('document' + event.detail);
});
if(document.implementation.hasFeature("CustomEvents", "3.0")){
event.document.createEvent("CustomEvent");
event.initCustomEvent("myevent", true, false, "hellow world");
div.dispatchEvent(event);
}