Yuming Ren Curious and Learning 平平无奇

Javascript Events

2017-08-10
yuming

1. 事件

事件是JavaScript和HTML DOM交互的基础,事件即可以是用户产生的,也可以是一些API产生的。DOM元素支持的每种事件,都可以使用一个与相应事件处理程序(事件监听)通过相应HTML属性来指定。添加DOM元素事件处理程序,可以使用DOM0级的方式添加,也可以使用DOM2级的方式添加。

事件是HTML文档执行某种操作时需要执行的动作,如:clickloadmouseover等都是事件的名字,响应事件的函数就叫做事件处理程序,又叫做事件监听器。

2. Event flow(事件流)

事件流表示页面中接收事件的顺序。因为Javascript开发时候IE与netscape的并行独立开发竞争,流产生了两种不同的事件流过程。

2.1. Event bubbling

由事件最底层的元素开始,逐渐往上到Ducument元素。

2.2. Event capturing

不大具体的节点先接收到事件,具体节点后接收到事件。用意在于事件到达预定目标之前就捕获它。

2.3. DOM event flow

W3C 制定了统一的标准,即DOM2的标准: 1.event capturing 2.处于目标阶段 3,event bubbling

此处介绍一个DOM2的方法,addEventListener(),addEventListener有三个参数:

 element.addEventListener(event, function, useCapture); 

第一个参数是需要绑定的事件; 第二个参数是触发事件后要执行的函数; 第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。我们举一个例子:

<div id="s1">s1 
  <div id="s2">s2</div> 
</div> 
<script> 
s1.addEventListener("click",function(e){ console.log("s1 Bubbling event"); },false);
s1.addEventListener("click",function(e){ console.log("s1 Capturing event"); },true);
s2.addEventListener("click",function(e){ console.log("s2 Capturing event"); },true); 
s2.addEventListener("click",function(e){ console.log("s2 Bubbling event"); },false); 

</script>

我们点击s2,得到的结果是

s1 Capturing event 
s2 Capturing event  
s2 Bubbling event 
s1 Bubbling event

这里记被点击的DOM节点为target节点,对于非target节点则先执行捕获在执行冒泡;对于target节点则是先执行先注册的事件,无论冒泡还是捕获。 此处点击s2,click事件从document->html->body->s1->s2(捕获前进), 这里在s1上发现了捕获注册事件,则输出”s1 Capturing event” 到达s2,已经到达目的节点target, s2上注册了冒泡和捕获事件,先注册的捕获后注册的冒泡,则先执行捕获,输出”s2 Capturing event” 再在target s2上执行后注册的事件,冒泡事件,输出”s2 Bubbling event” 下面进入冒泡阶段,按照s2->s1->body->html->document(冒泡前进) 在s1上发现了冒泡事件,则输出”s1 Bubbling event”

## 3.Event handler 事件处理程序

### 3.1 HTML event process 耦合在HMTL中的javscript可执行代码。这个方法可以调用在HTML页面其他地方定义的脚本,比如

  <script>
    function showMsg(msg){
        alert(msg);
    }
  </script>
  <input id = 'btn' type = 'button' value = 'click!' onclick="showMsg('clicked!')" /> 

响应某个event的函数, 名字以on开头。比如onclickonload

优缺点: todo

### 3.2 DOM0 event process DOM0 事件处理只有事件冒泡的过程。 DOM0 事件处理程序被认为是element的method。因此,this引用的是当前元素。因此可以在事件处理程序中访问元素的任何属性和方法。以这种方法添加的事件处理程序会在事件流的bubbling阶段被处理。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id); // "myBtn"
}

这个方法可以轻易的删除event handler,只要将事件处理程序的属性设置为null即可:

btn.onclick = null;

3.3 DOM2 event process

DOM2规范定义了两个方法:addEventListener()removeEventListener(),分别用于添加事件处理程序和删除事件处理程序。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名,做为事件处理程序的函数和一个布尔值。最后这个参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 fasle,表示在冒泡阶段调用事件处理程序。

var btn=document.getElementById("mybtn");   
btn.addEventListener("click",function(){   
alert(this.id);   
},false);  

使用 DOM2 级事件处理程序的主要好处是可以添加多个事件处理程序。

var btn = document.getElementById("myBtn");   
btn.addEventListener("click",function(){   
alert(this.id);   
},false);
btn.addEventListener("click",function(){ 
    alert("hello world!");
    },false);// 顺序触发

通过 addEventListener()添加的时间处理程序只能使用 removeEventListener()来移除,移除时传入的参数与添加时使用的参数相同。这也意味着通过 addEventListener()添加的匿名函数将无法移除。

var btn=document.getElementById("myBtn"); 

btn.addEventListener("click",function(){   
alert(this.id);   
},false);
// remove event
btn.removeEventListener("click",function(){
    alert(this.id);}
    ,false); // useless, as these 2 anonymous function are not same


4. event object事件对象

在触发的事件的函数里面我们会接收到一个event对象,通过该对象我们需要的一些参数,比如说我们需要知道此事件作用到谁身上了,就可以通过event的属性target来获取到(IE暂且不谈),或者想阻止浏览器的默认行为可以通过方法preventDefault()来进行阻止.以下是event对象的一些属性和方法

几种常用属性:

  1. type: 用于获取事件类型,可以用来通过一个函数处理多个事件,比如clickmouseovermouseout
// fetch btn object
var btn = document.getElementById('btn');
// handler funtion
var handler = function(event){
    swith( event.type ){
        case "click":
            alert("clicked");
            break;
        case "mouseover":
            alert("mouseover");
            break;
        case "mouseout":
            alert("mouseout");
            break;  
    }
}
//bind to event
btn.onclick = handler;
btn.onmousemove = handler;
btn.onmouseout = handler;

  1. target: 用户获取事件目标 事件加在哪个元素上。(更具体target.nodeName). 在事件委托event delegate中很常用.

  2. currentTarget:

currentTarget 属性永远指向this,即包含着当前正在处理事件的处理程序的那个元素。

currentTarget, target vs this event.currentTarget === this 总是成立的。

(function(){ 
     var color = 0;
     var o_ul = document.getElementById("ul_color"); 
     o_ul.addEventListener('click',function(event){ 
             console.log(event.target.innerHTML); // li inner
             console.log(event.currentTarget.innerHTML); // ul inner
             console.log(event.currentTarget === this); // always true
           }, false);
           
     var o_btn = document.getElementById("btn_add"); 
     o_btn.onclick = function(){ 
     var new_li = document.createElement('li'); 
     new_li.innerHTML = "newColor" + (++color); 
     o_ul.appendChild(new_li); 
     };        
 })();

常用方法:

  1. stopPropagation()方法 用于阻止事件冒泡,进而阻止后续捕获。
  2. preventDefault()方法 阻止事件的默认行为 移动端用的多.

比如阻止a链接的默认导航到href指向的URL。但是此处注意,只有canceleable属性设置为 true, 才可以用preventDefault()取消默认行为。

var a_link = document.getElementById("a_link");
a_link.onclick = function(e){
    e.preventDefault(); // prevent the default behavior of <a>
}

注:只有在事件处理程序执行期间,event object才会存在,一旦事件处理程序执行完成,event 对象就会被销毁。

5. Event Delegate 事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能; 原理: 事件委托是利用事件的冒泡原理来实现。具体元素的事件会冒泡到最外层的元素上,所以绑定在最外层元素上的事件也会触发,这就是事件委托,委托它们父级代为执行事件。 好处:

  1. 提高performance
  2. 处理动态添加的DOM元素
<ul id="ul_color"> 
  <li>red</li> 
  <li>yellow</li> 
  <li>blue</li> 
  <li>black</li> 
</ul>
// set a click event listener for each li
(function(){ 
    var o_ul = document.getElementById("ul_color"); 
    var o_li = o_ul.getElementsByTagName('li'); 
    console.log(o_li[0]);
    for(var i=0;i<o_li.length;i++){ 
        o_li[i].addEventListener('click',function(){ 
            console.log(this.innerHTML);
          }, false);
    } 
})();


// event delegate
(function(){ 
    var o_ul = document.getElementById("ul_color"); 
    o_ul.addEventListener('click',function(event){ 
            console.log(event.target.innerHTML);
          }, false);
})();

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发。此处值得注意的一点是event.target属性。target可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom。

//with new added dom elements
(function(){ 
    var color = 0;
    var o_ul = document.getElementById("ul_color"); 
    o_ul.addEventListener('click',function(event){ 
            console.log(event.target.innerHTML);
          }, false);
          
    var o_btn = document.getElementById("btn_add"); 
    o_btn.onclick = function(){ 
    var new_li = document.createElement('li'); 
    new_li.innerHTML = "newColor" + (++color); 
    o_ul.appendChild(new_li); 
    };
      
})();

注: focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

6. Event的兼容性

冒泡还是捕获

事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。所以addEventListener()的第三个参数常常设置为false. 要兼容旧版本的IE浏览器,可以使用IE的attachEvent函数

object.attachEvent(event, function)

两个参数与addEventListener相似,分别是事件和处理函数,默认是事件冒泡阶段调用处理函数,要注意的是,写事件名时候要加上”on”前缀(”onload”、”onclick”等)。

Jquery 事件

使用 jQuery 可以消除兼容性问题。参考 jQuery解决浏览器兼容性问题案例分析,jquery案例分析

7. ref

  1. javascript event(事件对象)详解
  2. 浅谈事件冒泡与事件捕获
  3. JavaScript DOM文档事件-DOM0、DOM2级事件处理程序
  4. javascript: 事件委托解析
  5. JavaScript事件捕获与事件冒泡原理 IE和DOM之间存在哪些主要差别

上一篇 BOM

下一篇 CSS笔记

Comments

Content