javaScirpt事件详解-原生事件基础(一)

事件

JavaScript与HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间,通过监听特定事件的发生,你能响应相关的操作。图片引用:UI Events

事件流

主要是当时的IE团队提出的事件流逝是事件冒泡流,而Netscape提出的是事件捕获流,
可以使用DOM2级定义的addEventListener()方法来处理在冒泡或者捕获阶段调用事件处理程序。

事件冒泡

即事件开始时由最具体的元素(文档中嵌套最深的节点)接收,然后逐级向上传播到较为不具体的节点。
1

事件捕获

即最外层的元素更早接收到事件,而最具体的元素应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。
2

DOM事件流

而“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,然后是实际的目标接收到事件,最后一个阶段是冒泡阶段。
3

addEventListener函数接收三个参数,要处理的事件名、作为事件处理程序的函数和一个布尔值,最后的布尔值如果是true,表示在不回阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。

事件捕获:

//html
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <div class="a">
    <div class="b">
      <button class="c">click</button>
    </div>
  </div>
</body>
</html>

var $body = document.querySelector("body"),
    $a = document.querySelector(".a"),
    $b = document.querySelector(".b"),
    $c = document.querySelector(".c");

$body.addEventListener("click",function(){
  console.log(this);
},true);
$a.addEventListener("click",function(){
  console.log(this);
},true);
$b.addEventListener("click",function(){
  console.log(this);
},true);
$c.addEventListener("click",function(){
  console.log(this);
},true);

单击button,则
4

事件冒泡:

$body.addEventListener("click",function(){
  console.log(this);
},false);
$a.addEventListener("click",function(){
  console.log(this);
},false);
$b.addEventListener("click",function(){
  console.log(this);
},false);
$c.addEventListener("click",function(){
  console.log(this);
},false);

单击button,则
5

上述代码在IE9和其他现代浏览器都支持(测试页)。IE8及更早版本不支持DOM事件流只有事件冒泡。所以我们有两个阶段可以在目标对象上操作事件,但为了兼容性考虑,一般用冒泡的情况比较多。

事件处理程序

事件是用户或浏览器自身执行的某种动作,而响应某个事件的函数就叫做事件处理程序(或事件侦听器)

HTML事件处理程序

即内联式处理函数,以"on"开头:

<button onclick="alert(this)"></button>

虽然方便,但有很大问题,首先,存在时差问题,因为用户可能会在HTML元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件;另一个缺点是:这样扩展事件处理程序的作用域链在不同浏览器会导致不同结果;而最后的缺点则是HTML与JavaScript代码紧密耦合,如果要更换事件处理程序,就要改动两个地方。

DOM0级事件处理程序

通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
每个元素(包括window和docuemnt)都有自己的事件处理程序属性,这些属性通常全部小写:

var btn = document.getElementById('myBtn');
btn.onclick = function(){
  alert("Clicked");
}

以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

DOM2级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点中都包含这两个方法,在上面的事件捕获和冒泡有用到。使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。

IE事件处理程序

IE实现的方法是:attachEvent()和detachEvent()。这两个方法接收相同的两个参数:事件处理程序名称与事件处理程序函数。通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
  alert("Clicked");
})

这里的参数中表行为的是onclick而非DOM的addEventListener()方法中的click,在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域,在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window

var btn = document.getElementById("myBtn");
btn.attachEvent('onclick',function(){
  alert(this === window);// true
});

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,所有浏览器都支持event对象,但支持方式不同。

DOM中的事件对象

event对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。不过,所有事件都有下表的成员属性。

属性/方法 类型 读/写 说明
bubbles boolean 只读 表明事件是否冒泡
cancelable boolean 只读 表明是否可以取消事件的默认行为
currentTarget element 只读 其事件处理程序当前正在处理事件的那个元素
defaultPrevented boolean 只读 为true表示已经调用了preventDefault()DOM3新增
datail integer 只读 与事件相关的细节信息
eventPhase integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标 3表示冒泡阶段
preventDefault() function 只读 取消事件的默认行为。如果cancelable是true,则可以使用这个方法
stopImmediatePropagation() function 只读 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级新增)
stopPropagation() function 只读 取消事件的进一步捕获或冒泡,如果bubbles为true,则可以使用这个方法
target element 只读 事件的目标
isTrusted boolean 只读 返回一个布尔值,表明当前事件是否是由用户行为触发,还是由一个脚本生成的
type string 只读 被触发的事件的类型
view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象

更详细的文档可以看 这里

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this,currentTarget和target包含相同的值。

根据event所拥有的属性,就可以更加细致的操作事件。在w3c的这篇Events中,我们可以知道哪些事件可以冒泡.
首先是冒泡事件,如果只需要在具体节点触发,而不冒泡到包裹层中,则可以使用与冒泡行为相关的一系列属性,举例:

<div class="a">
  <a href="http://www.cnblogs.com"></a>
</div>

在这个div中,如果div和a都有监听点击事件,那么在点击a标签的时候(默认事件流在已过捕获阶段),会先触发a的点击事件,然后是div的点击事件,最后是a标签的默认事件。如果不想冒泡,也就是说点击a时只触发a的点击事件,则可以使用stopPropagation()方法:

var $a = document.querySelector("a");
$a.addEventListener("click",function(event){
  if(event.bubbles){
    event.stopPropagation();
  }
});

也可以在处理程序里直接删除节点,也能阻止冒泡。
当然,有时我们不希望跳转页面,而是执行自定义的脚本,那就可以使用preventDefault()方法:

var $a = document.querySelector("a");
$a.addEventListener("click",function(event){
  if(event.bubbles){
    event.preventDefault();
    // do something
  }
});

有时我们可能会接手他人的代码,在不了解具体代码的情况下,如果只是单个独立事件需要修改,则可以在运行老的js代码之前,使用stopImmediatePropagation()方法。

$a.addEventListener('click',function(event){
  event.preventDefault();
  event.stopImmediatePropagation();
  // do something
})

$a.addEventListener("click",function(event){
  alert("这是a标签");
});

当然,冒泡并不是说是一件坏事,因为场景很多变,在某些场景下,冒泡也是很有用的,比如说事件代理(delegation)。

事件代理

有时我们需要对生成的节点附加操作,event总是需要先找到element,再进行注册,才可以进行事件监听,那么对于这些生成的节点来说,当然可以在生成之后再次对该节点重新注册事件。但如果是生成了同一个标记的节点呢?
那么我们不仅需要把之前注册的事件remove,还需要重新绑定之前的标记,否则会二次触发。这时候事件代理
就可以解决这一类问题。根据DOM事件流,我们可以知道,在节点冒泡时,可以一直往上冒泡,所以,我们完全可以把事件代理给点击节点的父节点来做,根据event.target属性,我们可以知道指向触发事件的对象到底是谁,也因此,不用再使用繁琐的重新注册事件,就可以直接操作子元素。像jQuery,Zepto中都有关于事件代理的处理方法,后篇会继续涉及到。

$div.addEventListener("click",function(event){
  alert("这是外层");
  if(event.target) {
    event.target.style.visibility = 'hidden';
  }
});

冒泡也会有副作用,mouseout事件会导致离开内部节点时提前触发,为此,jQuery自定义了mouseenter和mouseleave事件,Zepto也有相应的事件。
只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁(闭包)

IE中的事件对象

在IE中访问event,特别是IE6~8尤其注意,在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。

var btn = document.getElementById('a');
btn.onclick = function(){
  var event = window.event;
  alert(event.type); //"click"
}

而IE中的event对象都会包含下表的属性和方法:

属性/方法 类型 读/写 说明
cancelBubble Boolean 读/写 默认值为false,但将其设置为true就可以取消事件冒泡(与DOM中的stopPropagation()方法的作用相同)
returnValue Boolean 读/写 默认值为false,但将其设置为false就可以取消事件的默认行为(与DOM中的preventDefault()方法的作用相同)
srcElement Element 只读 事件的目标(与DOM中的target属性相同)
type String 只读 被触发的事件的类型

事件类型

不表

事件模拟

结合jQuery和Zepto源码总结。

部分用例:event

posted @ 2016-11-14 09:54  leomYili  阅读(1502)  评论(7编辑  收藏  举报