13. 事件

JavaScript与HTML之间的交互是通过事件实现的。可以使用侦听器预定事件。
在传统软件工程中被称为观察者模式。支持行为与外观间的松散耦合。

13.1 事件流

  • 事件流描述的是从页面中接收事件的顺序
    • IE 事件冒泡流
    • NetscapeCommunicator 事件捕获流
  • Event bubbling
    • 事件开始时由最具体的元素接受,然后逐级向上传递给较为不具体的节点
    • 现代浏览器,将事件一直冒泡到window对象
  • Event capturing
    • 不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件
    • 事件捕获的用意在于在事件到达预定目标之前捕获它。
    • DOM2规范要求事件应该从document开始传播,但浏览器都是从window开始捕获事件
  • DOM2事件
    • 事件流三个阶段:
      1. 事件捕获阶段
      2. 处于目标阶段
      3. 事件冒泡阶段

13.2 事件处理程序

  • 事件就是用户或浏览器自身执行的某种动作。
  • 响应事件的函数就是事件处理程序
  1. HTML事件处理
<button onclick="alert('event')"></button>
  1. DOM0事件处理
// 在事件流的冒泡阶段被处理
var button = document.getElementById('myBtn');
button.onclick = function(){
	alert(this.id); // "myBtn"
}
button.onclick = null;
  1. 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
      • 触发顺序与添加顺序相反
      • 全局作用域中执行事件处理程序

13.3 事件对象

  • 在DOM上触发某个事件时,会产生一个事件对象event
  • 所有事件通用成员
    • bubbles
    • cancelable
    • currentTarget
    • defaultPrevented
    • detail
    • eventPhase 事件阶段1,2,3
    • preventDefault() 取消默认行为
    • stopImmediatePropagation
    • stopPropagation()
    • target
    • trusted
    • type
    • view
  • currentTarget事件处理程序注册在哪个元素上
  • target事件真正的目标
  • 只有在事件处理程序执行期间,event对象才会存在,执行完毕就会被销毁。

13.***类型

  • DOM3事件:
    • UI事件
    • 焦点,元素获得失去焦点
    • 鼠标
    • 键盘
    • 滚轮
    • 文本,输入文本
    • 合成,IMEinput method editor
    • 变动mutation 底层DOM结构发生变化
  • UI事件
    • load window/img
    • unload 卸载
    • 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 --> 0
      • screenX/screenY/clientX/clientY
      • ctrlKey/altKey/shitKey/metaKey
      • button 按下哪个鼠标键,默认0
      • relatedTarget
    • 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);