13. 事件
JavaScript与HTML之间的交互是通过事件实现的。可以使用侦听器预定事件。
在传统软件工程中被称为观察者模式。支持行为与外观间的松散耦合。
13.1 事件流
- 事件流描述的是从页面中接收事件的顺序
- IE 事件冒泡流
- NetscapeCommunicator 事件捕获流
Event bubbling
- 事件开始时由最具体的元素接受,然后逐级向上传递给较为不具体的节点
- 现代浏览器,将事件一直冒泡到window对象
Event capturing
- 不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件
- 事件捕获的用意在于在事件到达预定目标之前捕获它。
- DOM2规范要求事件应该从document开始传播,但浏览器都是从window开始捕获事件
- DOM2事件
- 事件流三个阶段:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
- 事件流三个阶段:
13.2 事件处理程序
- 事件就是用户或浏览器自身执行的某种动作。
- 响应事件的函数就是事件处理程序
- HTML事件处理
<button onclick="alert('event')"></button>
- DOM0事件处理
// 在事件流的冒泡阶段被处理
var button = document.getElementById('myBtn');
button.onclick = function(){
alert(this.id); // "myBtn"
}
button.onclick = null;
- DOM2事件处理
addEventListener()
removeEventListener()
- 接受三个参数:
- 监听的事件名
- 作为事件处理程序的函数
- 调用处理函数的时机:
true
捕获阶段,false
冒泡阶段
- 【优势】可以添加多个事件处理程序
- 匿名事件处理函数,将不会被移除
var button = document.getElementById('myBtn');
button.addEventListener('click', function(){
alert(this.id);
}, false);
button.addEventListener('click', function(){
alert('hello world');
}, false);
// 添加了两个事件处理程序,按照添加顺序依次执行
// 移除事件只能通过removeEventListener(name, handler, flag)
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。
最好只在需要在时间到达目标之前截获他的时候,将事件处理程序加到捕获阶段
- IE事件处理程序
attachEvent()
detachEvent()
- 【差异】:
- IE中事件名称带有
on-
前缀,例如onclick
- 触发顺序与添加顺序相反
- 全局作用域中执行事件处理程序
- IE中事件名称带有
13.3 事件对象
- 在DOM上触发某个事件时,会产生一个事件对象
event
- 所有事件通用成员
bubbles
cancelable
currentTarget
defaultPrevented
detail
eventPhase
事件阶段1,2,3preventDefault()
取消默认行为stopImmediatePropagation
stopPropagation()
target
trusted
type
view
currentTarget
事件处理程序注册在哪个元素上target
事件真正的目标- 只有在事件处理程序执行期间,
event
对象才会存在,执行完毕就会被销毁。
13.***类型
- DOM3事件:
- UI事件
- 焦点,元素获得失去焦点
- 鼠标
- 键盘
- 滚轮
- 文本,输入文本
- 合成,IME
input method editor
- 变动
mutation
底层DOM结构发生变化
- UI事件
load
window/imgunload
卸载abort
error
select
resize
scroll
- 焦点事件
blur
不冒泡focus
元素获得焦点,但不冒泡focusin
获得焦点,冒泡focusout
冒泡
- 鼠标与滚轮事件
click
dbclick
mousedown
任意鼠标按钮按下mouseup
按键释放mouseenter
不冒泡mouseleave
光标移动到元素范围之外,不冒泡mousemove
鼠标指针在元素内部移动时重复触发mouseout
鼠标指针移入另一个元素(可以是外部,也可是子元素)mouseover
- 鼠标位置
event.clientX
客户区event.clientY
event.pageX
页面event.pageY
event.screenX
屏幕event.screenY
- 修改键
shift
,ctrl
,alt
, meta:windows
/cmd
- DOM规定状态值
shiftKey
,ctrlKey
,altKey
,metaKey
- 主目标和相关元素
event.relatedTarget
mouseover
事件主目标是获得光标的元素,相关元素是失去光标的元素mouseout
事件主目标是失去光标的元素,相关元素是获得光标的元素
- 鼠标按键
event.button
- 0左键,1中键,2右键
- 鼠标滚轮事件
mousewheel
wheelDelta
120的倍数,(+上-下)表示滚动方向
- 键盘与文本事件
keydown
keypress
keyup
keycode
键码key
键值textInput
inputMethod
- 能够区分键盘输入、粘贴、拖放、IME、手写、脚本等
- 复合事件
composition event
- 用于处理
IME
的输入序列 IME
通常需要同时按住多个键,最终只输入一个字符compositionstart
compositionupdate
compositionend
- 用于处理
- 变动事件
- DOM发生变化时
removeChild()
/replaceChild()
DOMNodeRemoved
—>DOMNodeRemovedFromDocument
—>DOMSubtreeModified
appendChild()
/replaceChild()
/insertBefore()
DOMNodeInserted
—>DOMNodeInsertedIntoDocument
—>DOMSubtreeModified
- HTML5事件
contextmenu
beforeunload
卸载页面之前DOMContentLoaded
在形成完整的DOM树之后就会触发,不理会图像、JavaScript、css文件是否已经下载完成readystatechange
uninitilized
loading
loaded
interactive
complete
- 往返缓存
back-forward cache
可以使用户使用浏览器的后退和前进按钮时加快页面的转换速度 haschange
URL参数列表变化
- 设备事件
device event
orientationchange
window.orientatin
0
肖像模式90
左旋90度-90
右旋90度
MozOrientation
deviceorientation
设备朝向devicemotion
- 触摸与手势事件
Touch Events
touchstart
touchmove
touchend
touchcancel
// 创建自定义上下文菜单
var menu = document.createElement("ul");
menu.innerHTML = ` <li> <a href="www.baidu.com" target="_blank">BaiDu</li> <li><a href="https://github.com/18202409203" target="_blank">Github</li> <li><a href="https://blog.csdn.net/pangji0417" target="_blank">CSDN</li> `;
menu.id = "myMenu";
menu.style.background = "#fff";
menu.style.border = "1px solid #000";
menu.style.color = "red";
menu.style.visibility = "hidden";
document.body.appendChild(menu);
function menuHandler(event){
event.preventDefault();
var mm = document.getElementById("myMenu");
mm.style.left = event.clientX + 'px';
mm.style.top = event.clientY + 'px';
mm.style.position = 'absolute';
mm.style.visibility = "visible";
}
function clickHandler(event){
var mm = document.getElementById("myMenu");
mm.style.visibility = 'hidden';
}
document.addEventListener("contextmenu", menuHandler);
document.addEventListener("click", clickHandler);
13.5 内存和性能
- 事件委托
- 不是为每个元素添加一个事件,因为会冒泡,所以只指定一个事件处理程序,在DOM树中尽量最高层次上管理某一类型的所有事件
<ul id="myLinks">
<li id="goSomewhere">Go Somewhere</li>
<li id="doSomething">Do Something</li>
</ul>
<script> var list = document.getElementById("myLinks"); list.addEventListener("click", handler); function handler(event){ var target = event.target; switch(target.id){ case "goSomewhere": // goSomewhere break; case "doSomething": // doSomething break; } } </script>
- 空事件处理程序
dangling event handler
- 如果带有事件处理程序的元素被
innerHTML
删除,那么原来添加到元素中的事件处理程序很有可能无法被当作垃圾收回
- 如果带有事件处理程序的元素被
13.6 模拟事件
- DOM中的事件模拟
document.createEvent(type)
创建事件UIEvent
MouseEvent
—>initMouseEvent()
MutationEvent
—>initMutationEvent()
KeyboardEvent
—>initKeyEvent()
initMouseEvent()
设置事件type
“click”bubbles
cancelable
view
-->document.defaultView
detail
--> 0screenX
/screenY
/clientX
/clientY
ctrlKey
/altKey
/shitKey
/metaKey
button
按下哪个鼠标键,默认0relatedTarget
initKeyEvent()
type
“keydown”bubbles
cancelable
view
key
键码location
键区modifiers
修改键列表repeat
按键次数
dispatchEvent()
触发事件
// 模拟右键菜单事件
var e = document.createEvent("MouseEvent");
e.initMouseEvent("contextmenu", true,true,document.defaultView,
100, 100, 100, 100, false,false,false, false,2, null);
document.dispatchEvent(e);