事件

一、什么是事件:

事件是您在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,您可以某种方式对事件做出回应。

浏览器赋予元素天生默认的一些行为,不论是否绑定相关的方法,只要进行相应的行为操作了,那么一定会触发相应的事件

事件是文档或者浏览器窗口中发生的,特定的交互瞬间。

事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字。

事件是javaScript和DOM之间交互的桥梁。

你若触发,我便执行——事件发生,调用它的处理函数执行相应的JavaScript代码给出响应。

典型的例子有:页面加载完毕触发load事件;用户单击元素,触发click事件。

 事件三要素:事件源(事件被触发的对象)、事件类型(什么事件,比如鼠标点击事件)和事件处理函数(针对某个事件要做出的相应)。

二、事件处理程序(事件处理器

响应某个事件的函数就叫事件处理程序(也叫事件处理函数事件句柄)。事件处理程序的名字以"on"开头,因此click事件的事件处理程序就是onclick,load事件的事件处理程序就是onload。

 

三、事件对象

什么是事件对象?

用我自己的话说,当某个事件触发时,浏览器就会创建一个对象,这个对象包含了该事件所有相关的属性和方法等有关信息,我们可以调用这个事件对象。

有人这么定义事件对象,说事件在浏览器中是以对象的形式存在的,触发一个事件,就会产生一个事件对象,该对象包含着所有与事件有关的信息,包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。这感觉就像是“手机”和“华为P7手机”的关系一样。

例如:鼠标操作产生的event中会包含鼠标位置的信息;键盘操作产生的event中会包含与按下的键有关的信息。

所有浏览器都支持event对象,但支持方式不同,在DOM中event对象必须作为唯一的参数传给事件处理函数,在IE中event是window对象的一个属性。

(一)、html事件处理程序中的event

  会创建一个包含局部变量event的函数。可通过event直接访问事件对象。

<input id="btn" type="button" value="click" onclick=" console.log('html事件处理程序'+event.type)"/>

(二)、DOM中的事件对象

  都会把event作为参数传入事件处理程序

  DOM中事件对象重要的属性和方法

    属性

    • type属性,用于获取事件类型
    • target属性 用户获取事件目标 事件加在哪个元素上。(主要在事件委托时使用)

    方法

    • stopPropagation()方法 用于阻止事件冒泡
    • preventDefault()方法 阻止事件的默认行为 移动端用的多
 1 <body>
 2 <input id="btn" type="button" value="click"/>
 3 <script>
 4     var btn=document.getElementById("btn");
 5     btn.onclick=function(event){
 6         console.log("DOM0 & click");
 7         console.log(event.type);    //click
 8     }
 9     btn.addEventListener("click", function (event) {
10         console.log("DOM2 & click");
11         console.log(event.type);    //click
12     },false);
13 </script>
14 </body>

(三)、IE中的事件对象

  第一种情况: 通过DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。

  第二种情况:通过attachEvent()添加的事件处理程序,event对象作为参数传入。

<body>
<input id="btn" type="button" value="click"/>
<script>
    var btn=document.getElementById("btn");
    btn.onclick= function () {
        var event=window.event;
       console.log(event.type); //click
    }
</script>
</body>
<body>
<input id="btn" type="button" value="click"/>
<script>
    var btn=document.getElementById("btn");
    btn.attachEvent("onclick", function (type) {
        console.log(event.type);    //click
    })
</script>
</body>这里有个大大的问题没搞懂

(四)、事件对象的公共成员

1、DOM中事件对象的公共成员

事件对象包含与创建它有关的属性和方法。所以事件类型不一样,可用的属性和方法不一样。但是,DOM中所有事件都有以下公共成员。【注意bubbles属性和cancelable属性】

属性/方法 类型 读/写 说明
bubbles Boolean 只读    表明事件是否冒泡
stopPropagation()*** Function 只读 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法
stopImmediatePropagation()  Function 只读 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级事件中新增)
cancelable Boolean 只读 表明是否可以取消事件的默认行为
preventDefault()*** Function 只读 取消事件的默认行为。如果cancelable是true,则可以使用这个方法
defaultPrevented Boolean 只读 为true表示已经调用了preventDefault()(DOM3级事件中新增)
currentTarget*** Element 只读 事件处理程序绑定的那个元素(currentTarget始终===this,即处理事件的元素
target*** Element 只读 直接事件目标,真正触发事件的目标
detail Integer  只读 与事件相关的细节信息
eventPhase*** Integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标阶段,3表示冒泡阶段
trusted Boolean 只读 为true表示事件是由浏览器生成的。为false表示事件是由开发人员通过JavaScript创建的(DOM3级事件中新增)
type*** String 只读 被触发的事件的类型
view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象

 

a、对比currentTarget和target

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只是包含事件的实际目标。

举例:页面有个按钮,在body(按钮的父节点)中注册click事件,点按钮时click事件会冒泡到body进行处理。

 1 <body>
 2 <input id="btn" type="button" value="click"/>
 3 <script>
 4     document.body.onclick=function(event){
 5         console.log("body中注册的click事件");
 6         console.log("this===event.currentTarget? "+(this===event.currentTarget)); //true
 7         console.log("currentTarget===document.body?"+(event.currentTarget===document.body)); //true
 8         console.log('event.target===document.getElementById("btn")? '+(event.target===document.getElementById("btn"))); //true
 9     }
10 </script>
11 </body>

b、通过type属性,可以在一个函数中处理多个事件。

原理:通过检测event.type属性,对不同事件进行不同处理。

举例:定义一个handler函数用来处理3种事件:click,mouseover,mouseout。

运行效果:点击按钮,弹出框。鼠标经过按钮,按钮背景色变为粉色;鼠标离开按钮,背景色恢复默认。

 1 <body>
 2 <input id="btn" type="button" value="click"/>
 3 <script>
 4 var handler=function(event){
 5     switch (event.type){
 6         case "click":
 7             alert("clicked");
 8             break;
 9         case "mouseover":
10             event.target.style.backgroundColor="pink";
11             break;
12         case "mouseout":
13             event.target.style.backgroundColor="";
14     }
15 };
16     var btn=document.getElementById("btn");
17     btn.onclick=handler;
18     btn.onmouseover=handler;
19     btn.onmouseout=handler;
20 </script>
21 </body>

 

c、stopPropagation()和stopImmediatePropagation()对比

同:stopPropagation()和 stopImmediatePropagation()都可以用来取消事件的进一步捕获或冒泡。

异:二者的区别在于当一个事件有多个事件处理程序时,stopImmediatePropagation()可以阻止之后事件处理程序被调用。

举例:

 1 <body>
 2 <input id="btn" type="button" value="click"/>
 3 <script>
 4     var btn=document.getElementById("btn");
 5     btn.addEventListener("click",function(event){
 6         console.log("buttn click listened once");
 7 //    event.stopPropagation();//取消注释查看效果
 8 //    event.stopImmediatePropagation();//取消注释查看效果
 9     },false);
10     btn.addEventListener("click",function(){
11         console.log("button click listened twice");
12     },false);
13     document.body.onclick= function (event) {
14         console.log("body clicked");
15     }
16 </script>
17 </body>

d、eventPhase

eventPhase值在捕获阶段为1,处于目标阶段为2,冒泡阶段为3。可以通过下面代码查看:

var btn=document.getElementById("btn");
btn.onclick= function (event) {
console.log(event.CAPTURING_PHASE); //1
console.log(event.AT_TARGET); //2
console.log(event.BUBBLING_PHASE); //3
}

(2)、IE中event的公共成员

IE中的event的属性和方法和DOM一样会随着事件类型的不同而不同,但是也有一些是所有对象都有的公共成员,且这些成员大部分有对应的DOM属性或方法。

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

 

  (五)总结JS事件对象(event)的一些兼容性写法   

获得event对象兼容性写法 
event || (event = window.event);
获得target兼容型写法 
event.target||event.srcElement
阻止浏览器默认行为兼容性写法 
event.preventDefault ? event.preventDefault() : (event.returnValue = false);
阻止冒泡写法 
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);
添加事件监听和取消事件监听

 //添加事件监听兼容函数
 function addHandler(target, eventType, handler){
     if(target.addEventListener){//主流浏览器
         addHandler = function(target, eventType, handler){
             target.addEventListener(eventType, handler, false);
          };
      }else{//IE
          addHandler = function(target, eventType, handler){
              target.attachEvent("on"+eventType, handler);
         };        
     }
     //执行新的函数,如果不在这里执行,则智能添加一个处理程序
     addHandler(target, eventType, handler);
 }
 //删除事件监听兼容函数
 function removeHandler(target, eventType, handler){
     if(target.removeEventListener){//主流浏览器
         removeHandler = function(target, eventType, handler){
             target.removeEventListener(eventType, handler, false);
         }        
     }else{//IE
         removeHandler = function(target, eventType, handler){
             target.detachEvent("on"+eventType, handler);
         }        
     }
     //执行新的函数
     removeHandler(target, eventType, handler);
 }
 

  

 

四、事件流:

定义:事件流是从页面中接收事件的顺序,事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM事件流。

DOM标准规定事件流包括三个阶段:

    • 事件捕获阶段;由根节点到达事件实际目标的阶段,实际目标在捕获阶段不会接收事件
    • 处于目标阶段;目标阶段事件处理会被看成是冒泡阶段的一部分
    • 事件冒泡阶段。事件又传播回文档。

 注:

1)、尽管“DOM2级事件”标准规范明确规定事件捕获阶段不会涉及事件目标,但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两次机会在目标对象上面操作事件。

2)、并非所有的事件都会经过冒泡阶段 。所有的事件都要经过捕获阶段和处于目标阶段,但是有些事件会跳过冒泡阶段:如,获得输入焦点的focus事件和失去输入焦点的blur事件。

两次机会在目标对象上面操作事件例子:

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <style>
 8     #outer{
 9         position: absolute;
10         width: 400px;
11         height: 400px;
12         top:0;
13         left: 0;
14         bottom:0;
15         right: 0;
16         margin: auto;
17         background-color: deeppink;
18     }
19     #middle{
20         position: absolute;
21         width: 300px;
22         height:300px;
23         top:50%;
24         left: 50%;
25         margin-left: -150px;
26         margin-top: -150px;
27         background-color: deepskyblue;
28     }
29     #inner{
30         position: absolute;
31         width: 100px;
32         height:100px;
33         top:50%;
34         left:50%;
35         margin-left: -50px;
36         margin-top: -50px;;
37         background-color: darkgreen;
38         text-align: center;
39         line-height: 100px;
40         color:white;
41     }
42     #outer,#middle,#inner{
43         border-radius:100%;
44     }
45 </style>
46 <body>
47 <div id="outer">
48     <div id="middle">
49         <div id="inner">
50             click me!
51         </div>
52     </div>
53 </div>
54 <script>
55     var innerCircle= document.getElementById("inner");
56     innerCircle.addEventListener("click", function () {
57         alert("innerCircle的click事件在捕获阶段被触发");
58     },true);
59     innerCircle.addEventListener("click", function () {
60         alert("innerCircle的click事件在冒泡阶段被触发");
61     },false);
62     var middleCircle= document.getElementById("middle");
63     middleCircle.addEventListener("click", function () {
64         alert("middleCircle的click事件在捕获阶段被触发");
65     },true);
66     middleCircle.addEventListener("click", function () {
67         alert("middleCircle的click事件在冒泡阶段被触发");
68     },false);
69     var outerCircle= document.getElementById("outer");
70     outerCircle.addEventListener("click", function () {
71         alert("outerCircle的click事件在捕获阶段被触发");
72     },true);
73     outerCircle.addEventListener("click", function () {
74         alert("outerCircle的click事件在冒泡阶段被触发");
75     },false);
76 </script>
77 </body>
78 </html>
View Code

 运行效果就是会陆续弹出6个框,为说明原理我整合成了一个图:

 

 

五、事件类型

(一)、按级别划分:

1、HTML事件

事件直接加在html元素上。

  首先,这种方法已经过时了。因为动作(javascript代码)和内容(html代码)紧密耦合,修改时即要修改html也要修改js。但是写个小demo的时候还是可以使用的。

这种方式也有两种方法,都很简单:

第一种:直接在html中定义事件处理程序及包含的动作。

<input type="button" value="click me!" onclick="alert('clicked!')"/>

第二种:html中定义事件处理程序,执行的动作则调用其他地方定义的脚本。

1 <input type="button" value="click me!" onclick="showMessage()"/>
2 <script>
3 function showMessage(){
4     alert("clicked!");
5 }
6 </script>

:

1)通过event变量可以直接访问事件本身,比如onclick="alert(event.type)"会弹出click事件。

2)this值等于事件的目标元素,这里目标元素是input。比如 onclick="alert(this.value)"可以得到input元素的value值。

 

2、DOM0级事件(无法绑定多个处理函数,后定义的处理函数会覆盖前面定义的函数)

      绑定事件:element.onclick=function(){}

      解绑事件:element.onclick=null;

3、DOM1没有跟事件相关的更新

4、DOM2级事件(可以绑定多个处理函数,所有的DOM节点都包含这2个方法。

  绑定事件:element.addEventListener('click', 函数名,  false);

  解绑事件:element.removeEventListener('click', 函数名, false);

  function 函数名(){};

  注:通过addEventListener添加的匿名函数将无法删除。

       为了能删除事件处理程序,代码可以这样写:

<input id="myBtn" type="button" value="click me!"/>
<script>
    var myBtn=document.getElementById("myBtn");
    var handler=function(){
        alert("hello");
    }
    myBtn.addEventListener("click",handler,false);
    myBtn.removeEventListener("click",handler,false);
</script>

 

5、DOM3级事件 (定义了自定义事件)

   element.addEventListener('keyup', function(){}, false)

  

 (二)、按事件类型划分

1、鼠标事件

    1. click:用户单击主鼠标按钮(一般是左键)或者按下在聚焦时按下回车键时触发
    2. dblclick:用户双击主鼠标按键触发(频率取决于系统配置)
    3. mousedown:用户按下鼠标任意按键时触发
    4. mouseup:用户抬起鼠标任意按键时触发
    5. mousemove:鼠标在元素上移动时触发
    6. mouseover:鼠标进入元素时触发
    7. mouseout:鼠标离开元素时触发
    8. mouseenter:鼠标进入元素时触发,该事件不会冒泡
    9. mouseleave:鼠标离开元素时触发,该事件不会冒泡

      区别:
      over和out,不考虑子元素,从父元素移动到子元素,对于父元素而言,仍然算作离开
      enter和leave,考虑子元素,子元素仍然是父元素的一部分
      mouseenter和mouseleave不会冒泡
      事件对象
      所有的鼠标事件,事件处理程序中的事件对象,都为 MouseEvent

      altKey:触发事件时,是否按下了键盘的alt键
      ctrlKey:触发事件时,是否按下了键盘的ctrl键
      shiftKey:触发事件时,是否按下了键盘的shift键
      button:触发事件时,鼠标按键类型
      0:左键
      1:中键
      2:右键
      位置
      page:pageX、pageY,当前鼠标距离页面的横纵坐标
      client: clientX、clientY,鼠标相对于视口的坐标
      offset:offsetX、offsetY,鼠标相对于事件源的内边距的坐标
      screen: screenX、screenY,鼠标相对于屏幕
      x、y,等同于clientX、clientY
      movement:movementX、movementY,只在鼠标移动事件中有效,相对于上一次鼠标位置,偏移的距离

2、键盘事件

3、表单事件

4、html5事件

5、window全局对象

 

IE事件兼容问题

IE8及以下浏览器不支持addEventListener,在实际开发中如果要兼容到IE8及以下浏览器。如果用原生的绑定事件,需要做兼容处理,可利用jquery的bind代替。

IE8及以下版本浏览器实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。

这两个方法都需要两个参数:事件处理程序名称事件处理程序函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。注意是事件处理程序名称而不是事件名称,所以要加上on,是onclick而不是click。

:

IE11只支持addEventListener!

IE9,IE10对attachEvent和addEventListener都支持!

TE8及以下版本只支持attachEvent!

可以拿下面代码在IE各个版本浏览器中进行测试。

 1 <input id="myBtn" type="button" value="click me!"/>
 2 <script>
 3     var myBtn=document.getElementById("myBtn");
 4     var handlerIE=function(){
 5         alert("helloIE");
 6     }
 7     var handlerDOM= function () {
 8         alert("helloDOM");
 9     }
10     myBtn.addEventListener("click",handlerDOM,false);
11     myBtn.attachEvent("onclick",handlerIE);
12 </script>

添加事件处理程序:现在为按钮添加两个事件处理函数,一个弹出“hello”,一个弹出“world”

1 <script>
2     var myBtn=document.getElementById("myBtn");
3     myBtn.attachEvent("onclick",function(){
4         alert("hello");
5     });
6     myBtn.attachEvent("onclick",function(){
7         alert("world");
8     });
9 </script>

注:这里运行效果值得注意一下:

IE8以下浏览器中先弹出“world”,再弹出“hello”。和DOM中事件触发顺序相反。

IE9及以上浏览器先弹出“hello”,再弹出“world”。和DOM中事件触发顺序相同了。

可见IE浏览器慢慢也走上正轨了。。。

删除事件处理程序:通过attachEvent添加的事件处理程序必须通过detachEvent方法删除,且参数一致。

和DOM事件一样,添加的匿名函数将无法删除。

所以为了能删除事件处理程序,代码可以这样写:

1 <input id="myBtn" type="button" value="click me!"/>
2 <script>
3     var myBtn=document.getElementById("myBtn");
4     var handler= function () {
5         alert("hello");
6     }
7     myBtn.attachEvent("onclick",handler);
8     myBtn.detachEvent("onclick",handler);
9 </script>

note:IE事件处理程序中还有一个地方需要注意:作用域

使用attachEvent()方法,事件处理程序会在全局作用域中运行,因此this等于window。

而dom2或dom0级的方法作用域都是在元素内部,this值为目标元素。

下面例子会弹出true。

<input id="myBtn" type="button" value="click me!"/>
<script>
    var myBtn=document.getElementById("myBtn");
    myBtn.attachEvent("onclick",function(){
        alert(this===window);
    });
</script>

在编写跨浏览器的代码时,需牢记这点。

IE7\8检测:

 1 //判断IE7\8 兼容性检测
 2             var isIE=!!window.ActiveXObject;
 3             var isIE6=isIE&&!window.XMLHttpRequest;
 4             var isIE8=isIE&&!!document.documentMode;
 5             var isIE7=isIE&&!isIE6&&!isIE8;
 6              
 7             if(isIE8 || isIE7){
 8                 li.attachEvent("onclick",function(){
 9                     _marker.openInfoWindow(_iw);
10                 })    
11             }else{
12                 li.addEventListener("click",function(){
13                     _marker.openInfoWindow(_iw);
14                 })
15             }

 

 

焦点事件

鼠标事件

六、事件代理(事件委托)

 传统的事件处理中,需要为每个元素添加事件处理器。js事件代理则是一种简单有效的技巧,通过它可以把事件处理器添加到一个父级元素上,从而避免把事件处理器添加到多个子级元素上。

 

1、事件代理

事件代理的原理用到的就是事件冒泡和目标元素,把事件处理器添加到父元素,等待子元素事件冒泡,并且父元素能够通过target(IE为srcElement)判断是哪个子元素,从而做相应处理。

传统事件处理,为每个元素添加事件处理器,代码如下:

 1 <body>
 2 <ul id="color-list">
 3 <li>red</li>
 4 <li>orange</li>
 5 <li>yellow</li>
 6 <li>green</li>
 7 <li>blue</li>
 8 <li>indigo</li>
 9 <li>purple</li>
10 </ul>
11 <script>
12 (function(){
13     var colorList=document.getElementById("color-list");
14     var colors=colorList.getElementsByTagName("li");
15     for(var i=0;i<colors.length;i++)
16     {
17         colors[i].addEventListener('click',showColor,false);
18     };
19     function showColor(e)
20     {
21         e=e||window.event;
22         var targetElement=e.target||e.srcElement;
23         alert(targetElement.innerHTML);
24     }
25 })();
26 </script>
27 </body>
View Code

事件代理的处理方式,代码如下:

 1 <body>
 2 <ul id="color-list">
 3 <li>red</li>
 4 <li>orange</li>
 5 <li>yellow</li>
 6 <li>green</li>
 7 <li>blue</li>
 8 <li>indigo</li>
 9 <li>purple</li>
10 </ul>
11 <script>
12 (function(){
13     var colorList=document.getElementById("color-list");
14     colorList.addEventListener('click',showColor,false);
15     function showColor(e)
16     {
17         e=e||window.event;
18         var targetElement=e.target||e.srcElement;
19         if(targetElement.nodeName.toLowerCase()==="li"){
20         alert(targetElement.innerHTML);
21         }
22     }
23 })();
24 </script>
25 </body>
View Code

2、事件代理的好处

 总结一下事件代理的好处:

  • 将多个事件处理器减少到一个,因为事件处理器要驻留内存,这样就提高了性能。想象如果有一个100行的表格,对比传统的为每个单元格绑定事件处理器的方式和事件代理(即table上添加一个事件处理器),不难得出结论,事件代理确实避免了一些潜在的风险,提高了性能。
  • DOM更新无需重新绑定事件处理器,因为事件代理对不同子元素可采用不同处理方法。如果新增其他子元素(a,span,div等),直接修改事件代理的事件处理函数即可,不需要重新绑定处理器,不需要再次循环遍历。

3、事件代理的问题:

代码如下:事件代理同时绑定了li和span,当点击span的时候,li和span都会冒泡。

<li><span>li中的span的内容</span></li>

<script>
    $(document).on('click', 'li', function(e){
        alert('li li');
    });

    $(document).on('click', 'span', function(e){
        alert('li span');
    })
</script>

解决办法:

方法一:span的事件处理程序中阻止冒泡

$(document).on('click', 'span', function(e){
        alert('li span');
        e.stopPropagation();
    })

方法二:li的事件处理程序中检测target元素

$(document).on('click', 'li', function (e) {
        if (e.target.nodeName == 'SPAN') {
            e.stopPropagation();
            return;
        }
        alert('li li');
    });

4、事件代理的一个有趣应用

点击一个列表时,输出对应的索引

<script>
    var ul=document.querySelector('ul');
    var lis=ul.querySelectorAll('ul li');
    ul.addEventListener('click', function (e) {
        var target= e.target;
        if(target.nodeName.toUpperCase()==='LI'){
            alert([].indexOf.call(lis,target));
        }
    },false)
</script>

 

举例:

页面加载事件

JS:window.onload = function(){};-----函数表达式结构创建函数
当页面所有资源加载完毕之后执行,无法同时绑定多个处理程序–DOM0级
DOM2级加载:

document.addEventListener("DOMContentLoaded",function(){
    alert(2)
},false);

 

posted on 2019-12-19 18:47  前端码牛  阅读(453)  评论(0编辑  收藏  举报

导航