Skip to content

事件梳理(一) #6

@superdc

Description

@superdc

事件

事件模型

事件是JavaScript和HTML交互的方式,比如常用的clickblurDOMContentLoadedmousedownmouseup事件等。我们会先订阅这些事件(被观察者),给事件指定事件处理程序,当事件发生时浏览器就会调用相应的处理程序进行处理。可以说,事件模型是观察者模式的具体实现。

事件处理机制

图片引用自官方文档
wx20180430-221404 2x
标准的DOM Events把事件传播分为三个阶段:

  1. 捕获阶段(Capture Phase)
  2. 处理目标阶段(At Target Phase)
  3. 冒泡阶段(Bubbling Phase)
    由于IE在9以上才支持事件捕获,为了兼容性,我们更多让事件在冒泡阶段触发。

指定Event Handler(事件处理程序)的几种方式

Event Handler的名字:on<event>,比如onclick

  1. HTML attribute
    用元素与Event Handler同名的属性来指定,比如像下面这样
<input value="Click me" onclick="alert(this.value)" type="button" id="button">

a. 因为HTML属性时大小写不敏感的,这里onclick也可以写为onClick,ONCLICK,ONClick... 但还是建议使用lowercase.
b. 这种方式指定的事件处理程序会创建一个包含属性内容的函数,等价于

button.onclick = function(){
    // this指向当前元素
    alert(this.value); 
    // 可以取到event对象
    alert(event.type);
}
// 这也提醒我们属性内容应该是一个函数调用

c. 这种方式是不被推荐的。
2. DOM property(DOM0级事件处理程序)

<input id="elem" type="button" value="Click me">
<script>
  var elem = document.getElementById('elem');
  // 指定事件处理程序
  elem.onclick = function() {
    // this指向当前元素
    alert(this.value);
    // 可以取到event对象
    alert(event.type);
  };
  // 删除事件处理程序
  elem.onclick = null;
</script>

a. DOM属性时大小写敏感的,所以属性只能写为onclick
b. 事件处理程序在冒泡阶段触发
c. 只能指定一个事件处理程序,否则会覆盖
3. addEventListener(DOM2级事件处理程序)

// 旧语法
target.addEventListener(type, listener[, useCapture]);
  • listener是函数,必需
  • useCapture是可选值,默认为false。为true表示listener在捕获阶段触发,为false则表示在冒泡阶段触发
// 新语法
target.addEventListener(type, listener[, options]);
  • listener可以为函数,可以为对象(对象中实现了EventListener接口,即拥有handleEvent方法)
  • options对象为可选,包括如下几个选项
    • capture 默认为false,同上面的useCapture
    • once 默认为false,表示listener最多调用一次,触发后即被自动移除(Firefox50, Chrome55开始支持)
    • passive 默认为false,表示不会禁止事件的默认行为,即不会调用preventDefault方法(Firefox49,Chrome51开始支持)
      passive是为了解决touchwheel事件引起的滚动卡顿。因为touchstart, touchmove都是可以cancelable的,它们的默认行为是滚动页面,可以通过preventDefault阻止默认行为。不管是否阻止默认行为,浏览器会在执行事件处理程序后再执行默认行为,这就有可能造成滚动卡顿,即使事件处理程序是一个空函数。当设置{passive: true},浏览器在监听事件的时候就知道了事件处理程序不会调用preventDefault,浏览器就可以毫无顾虑的执行默认行为了。

a. 这种方式允许为同一个事件添加多个事件处理程序
b. 多个事件处理程序按照添加的顺序执行

removeEventListener(移除事件处理程序)

target.removeEventListener(type, listener[, options]);
target.removeEventListener(type, listener[, useCapture]);

移除事件处理程序只检查type, listener, capture/useCapture,匿名的事件处理程序无法被移除。

element.addEventListener("mousedown", handleMouseDown, { passive: true });

element.removeEventListener("mousedown", handleMouseDown, { passive: true });     // Succeed
element.removeEventListener("mousedown", handleMouseDown, { capture: false });    // Succeed
element.removeEventListener("mousedown", handleMouseDown, { capture: true });     // Fail
element.removeEventListener("mousedown", handleMouseDown, { passive: false });    // Succeed
element.removeEventListener("mousedown", handleMouseDown, false);                 // Succeed
element.removeEventListener("mousedown", handleMouseDown, true);                  // Fail
  1. IE事件处理程序
//添加事件处理程序
target.attachEvent(eventNameWithOn, callback);

elem.attchEvent('onclick', function(){
  // this是window, 不是element
  console.log(this===window);  // true
});
//删除事件处理程序
target.detachEvent(eventNameWithOn, callback);

a. 可以为同一事件添加多个事件处理程序
b. 多个事件处理程序会以与添加顺序相反的顺序执行

参考资料

  1. 《JavaScript高级程序设计》
  2. http://javascript.info/introduction-browser-events
  3. https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
  4. https://www.cnblogs.com/ziyunfei/p/5545439.html
  5. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions