事件流

事件流描述了页面接收事件的顺序。

graph LR
A(IE) --从内而外--> B(事件冒泡流)
C(Netscape Communicator) --从外而内--> D(事件捕获流)
D --> 得到所有浏览器的支持

DOM2 Event规范规定事件流分为3个阶段:事件捕获、到达目标和事件冒泡

事件捕获和事件冒泡本质上是差不多的,我本身没有事件,我可以向内(子级元素)或向外(父级元素)寻找事件作为触发的事件。

事件处理程序

指定事件处理程序的方式如下

HTML事件处理程序

使用事件处理程序的名字以HTML属性的形式指定

<input type="button" value="Click Me" onclick="console.log(event.type)">

DOM0事件处理程序

把一个函数赋值给DOM元素的一个事件处理程序

let btn=document.getElementById('mybtn');

btn.onclick() = () => {
  ...
};

移除事件

btn.onclick=null;

DOM2事件处理程序

addEventListener(eventType,handler,flag)

graph LR
flag --> true:在捕获阶段调用handler
flag --> false:默认,在冒泡阶段调用handler

removeEventListener(eventType,handler,flag)

IE事件处理程序

attachEvent(eventType,handler)和detachEvent(eventType,handler)

使用attachEvent和使用DOM0方式的主要区别:attachEvent里的this值是window,而使用DOM0方式的事件处理程序里的this值是目标元素

跨浏览器事件处理程序

自行编写跨浏览器兼容的事件处理程序:addHandler(element,eventType,handler)和

removeHandler(element,eventType,handler)

const EventUtil={
  addHandler(element,eventType,handler){
    if(element.addEventListener){
      element.addEventListener(type,handler,false);
    }else if(element.attachEvent){
      element.attachEvent('on'+type,handler);
    }else{
      element['on'+type]=handler;
    }
  },
  removeHandler(element,eventType,handler){
    if(element.removeEventListener){
      element.removeEventListener(type,handler,false);
    }else if(element.detachEvent){
      element.detachEvent('on'+type,handler);
    }else{
      element['on'+type]=null;
    }
  }
}

事件对象

事件的所有信息被存储在一个名为 event 的对象中

DOM事件对象

不管以哪种方式指定事件处理程序,event对象是传给事件处理程序的唯一参数(如果要传参的话)

event属性:

属性/方法 类型 读/写 说明
type 字符串 只读 被触发的事件类型
currentTarget 元素 只读 当前事件处理程序所在元素
target 元素 只读 事件目标

在事件处理程序内部,this等于currentTarget,而target是事件的实际目标

IE事件对象

IE事件对象基于事件处理程序被指定的方式以不同方式来访问

  1. 使用DOM0方式指定,则 event 是window对象的一个属性
  2. 使用attachEvent()指定,则event作为唯一参数传给事件处理程序

跨浏览器事件对象

事件类型

DOM3 Event定义了如下事件类型:

  1. 用户界面事件:涉及与BOM交互的通用浏览器事件
  2. 焦点事件
  3. 鼠标事件
  4. 滚轮事件
  5. 输入事件
  6. 键盘事件
  7. 合成事件:在使用某种IME(Input Method Editor,输入法编辑器)输入字符时触发

用户界面事件

焦点事件

鼠标和滚轮事件

键盘与输入事件

合成事件

变化事件

HTML5事件

设备事件

触摸及手势事件

内存与性能

创建GUI的语言如C#给每一个按钮都设置了onclick事件处理程序,这样不会有什么性能损耗

但是JS不同:

  1. 每一个函数都是对象,都占用内存空间
  2. 为指定事件处理程序而访问DOM次数过多会造成整个页面交互延迟

事件委托

"过多事件处理程序"的解决办法就是事件委托

事件委托利用事件冒泡,只使用一个事件处理程序来处理一个类型的事件,这一个事件处理程序应位于祖先节点上,

删除事件处理程序

导致事件处理程序常驻内存的两个场景:

  1. 删除了带有事件处理程序的元素,而事件处理程序还在内存。最好在删除元素前,将其带有的事件处理程序一并删除( 置null );
  2. 第二场景就是页面卸载(前进、后退和刷新)。最好在onload事件处理程序中趁页面未卸载先删除其他所有事件处理程序

模拟事件

可以通过JS在任何时候触发任意事件,这些事件被当作浏览器创建的事件

DOM模拟事件

document.createEvent(eventType):创建一个event对象

在DOM2里,eventType都是英文复数形式;在DOM3里又改为了单数形式

eventType可取值:

  1. "UIEvents":通用用户事件(鼠标事件和键盘事件都继承自这个事件)
  2. "MouseEvents":通用鼠标事件
  3. "HTMLEvents":通用HTML事件

事件模拟的最后一步是触发事件,为此调用dispatchEvent(event)方法,触发模拟事件

IE模拟事件