js 事件详解1--事件流

本文详细介绍了事件流的概念,包括事件冒泡与事件捕获两种传播方式,并对比了不同浏览器的支持情况。此外还讲解了四种事件处理程序的使用方法,如html事件处理程序、DOM0级事件处理函数等。最后探讨了事件对象的使用技巧和事件委托的应用。

第一次在segmentfault写文章,希望通过这种方式来巩固所学的知识,也欢迎童鞋们指正其中有不对和错误的地方。^+^

  1. 事件流

事件流:页面中接收事件的顺序,即当一个事件发生时,该事件的传播过程便叫做事件流

事件流的种类

事件冒泡

事件由最具体的元素开始逐级向上传播至较为不具体的节点(文档)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="box">点我</div>
</body>
</html>

当我们点击id为box的div时,该点击事件传播顺序如下

div --> body --> html -->document

特别说明:IE5.5及更早的版本将绕过html节点直接到document,IE9,Firefox,chrome和safari将冒泡到window对象

事件捕获

事件捕获和事件冒泡似乎截然相反,由不太具体的节点先接收到事件 -->再到最具体的节点。同样还是用上面冒泡例子,则事件的传播顺序则是:
document --> html -->body -->div

特别说明:ie8之前不支持事件捕获,IE9,safari,chrome,opera,firefox目前支持良好。并且这些浏览器不是从document开始捕获,而是从window对象开始。

DOM事件流阶段

  1. 捕获阶段

  2. 目标阶段

  3. 冒泡阶段

事件流图解

以上面的代码为例子,由图可以很清晰地看出首先发生的是事件捕获-->实际的目标接收事件-->事件冒泡

特别说明:在DOM事件流中,实际的目标不会在捕获阶段接收到事件,即捕获阶段到body就停止,"下一阶段"是目标阶段,该阶段可以看成是事件冒泡的一部分,最终事件又被传播会document。
BUT :我们的各大浏览器总是不喜欢按照规范来,IE9,Safari,chrome,firefox及其更高的版本中都会在捕获阶段出发事件对象上的事件,最后导致有两个机会在目标对象上操作事件。

  1. 事件处理程序

    事件:用户或者浏览器自身执行的某个动作,比如load,click,mousemove等
    事件处理程序:相应某个事件的函数叫做事件处理函数(也叫做事件侦听器

事件处理程序类别

1 html事件处理程序:某个元素支持的某个事件可以用与事件处理程序同名的html特性来指定,该特性的值是能够执行的javascript代码。

<input type="button" value="点击" onclick="alert('我被点击了')" />

/*
   当点击该按钮的时候,浏览器会弹出'我被点击了';
*/

当然也可以给onclick赋值页面中其他地方定义的脚本

<script>
    function show(){
        alert('我被点击了');
    }

/*
  点击后也会弹出 '我被点击了'
*/

</script>
<input type="button" value="点击" onclick="show()" />

优点:简单明了,省去获取元素等一系列前提操作
缺点:html代码与js代码高度耦合,不符合分离原则


2 DOM0级别事件处理函数:使用 element.on[eventname]=fn的方式给元素添加事件

<input type="button" value="点击" id="click" />
<script>
    var oBtn=document.getElementById('click');
        //该方式被认为是元素的方法,即事件处理程序在元素的作用域中进行,this即该元素本身
        oBtn.onclick=function(){
            alert(this.id);//click
        }
        //注意:删除该事件处理程序可以用如下方法
        oBtn.onclick=null;//即点击后不再有任何反应       
</script>

3 DOM2级事件处理程序:DOM2级添加了addEventListener(添加事件处理程序)和removeEventListener(移除事件处理程序)
添加事件处理函数

<input type="button" value="点击" id="click" />
<script>
    var oBtn=document.getElementById('click');
        /* 参数1 指定事件名称...click mouseover mouseout
           参数2 事件处理程序(匿名函数或者有名函数)
           参数3 true(捕获阶段发生) or false(冒泡阶段发生)
        */
        oBtn.addEventListener('click',function(){
            alert(this.id)//click  this指的是该元素作用域内
        },false)
        //注意该种方式可以给一个函数添加多个事件处理函数,执行顺序与添加顺序相同
        oBtn.addEventListener('click',function(){
            alert('Hello World')//click
        },false)
         
</script>

移除事件处理函数:如果事件处理函数是有名函数,则可以通过名字来移除,匿名函数无法移除。

<input type="button" value="点击" id="click" />
<script>
 var oBtn=document.getElementById('click');
        function showId(){
            alert(this.id);
        };
        function HellowWorld(){
            alert('HellowWorld');
        }
        oBtn.removeEventListener('click',showId,false)
        //最后只能弹出HellowWorld
</script>        

4IE事件处理程序:ie实现了与dom类似的两个方法,attachEvent(添加),detachEvent(删除)
添加事件处理函数

<input type="button" value="点击" id="click" />
<script>
    var oBtn=document.getElementById('click');
            function showId(){
                alert(this.id);
            };
        oBtn.attachEvent('onclick',showId);//这时候会报错,因为这里的是在window
        的作用域内
        //修改如下
        oBtn.attachEvent('onclick',function(){
            showId.call(this);//改变this指向
        }) 
</script>

删除事件处理函数

<input type="button" value="点击" id="click" />
<script>
    var oBtn=document.getElementById('click');
            function showId(){
                alert('HelloWorld');
            };
        oBtn.attachEvent('onclick',showId);//这时候会报错,因为这里的是在window
        的作用域内
        //修改如下
        oBtn.detachEvent('onclick',showId) ;//点击没有任何反应
</script>

事件函数封装

绑定 为了解决attachEvent的this指向问题,并且可以通过有名称的函数来解除事件绑定,现在处理如下

//
   function bind(obj,eventName,fn){
        var _fn=fn;
        fn=function(){
            _fn.call(obj);//改变this指向
        };
        if(obj.addEventListener){
            obj.addEventListener(eventName,fn,false);
        }else{
            obj.attachEvent('on'+eventName,fn);
        }
        return fn;//用于事件解除
    }

解除

     function unbind(obj,eventName,fn){
        if(obj.removeEventListener){
            obj.removeEventListener(eventName,fn);
        }else{
            obj.detachEvent('on'+eventName,fn);
        }
    }

使用方式

    //给input添加和移除事件
    <input type="button" id="box" value="事件封装" />
    //添加
    function show( ){
        alert(this);
    }
    function show2( ){
        alert(this.id);
    }
    
    var removeFn=bind('box','click',show);//需要移除的事件处理程序,不是原程序名称show
    bind('box','click',show2);
    unbind('box','click',removeFn);
    //最后只会弹出 box
    

事件对象

当触发DOM上面的某个事件的时候,会产生一个事件对象event,这个对象中包含着所有与事件对象有关的信息。例如该事件类型导致事件的元素

DOM中的事件对象

DOM中的事件对象:兼容DOM的浏览器会将event对象传入到事件处理程序中,无论指定事件处理程序用什么方式(html方式,DOM0级方式,DOM2级方式)

    <input type="button" id="box" value="DOM中的事件对象" onclick="console.log(event.type)">
    //html方法  click
   var oBox=document.getElementById('box');
    //DOM0级别方法 click
        oBox.onclick=function(ev){
            console.log(ev.type);//click
        }
    ///DOM2级别方法 click
    oBox.addEventListener('click',function(ev){
            console.log(ev.type);//click
        })     

总的来说event对象包含与创建他的特定事件有关的属性和方法,但是触发的事件类型不同,则可用的属性和方法也不一样。但是都会包含以下成员

属性/方法类型读/写 说明
currentTargetelement只读事件处理程序当前正在处理程序的那个元素,我的理解是事件的直接绑定者
targetelement只读事件的目标
cancelableboolean只读表示是否可以取消事件的默认行为
preventDefault()function只读取消事件的默认行为 ,前提是cancelable为true
bubblesboolean只读表明事件是否可以冒泡
stopPropagation()function只读取消事件的进一步冒泡或者捕获,前提是bubbles为true
typeboolean只读事件类型
viewabstractView只读与事件关联的抽象视图,等同于发生事件的window对象
detailinteger只读与事件相关的细节信息
eventPhaseinteger只读调用事件处理程序的阶段,1::捕获,2:“处于目标”,3:冒泡
trustedboolean只读为true表示事件是由浏览器生成的,为false表示事件是由开发人员通过js生成的。(DOM3)
stopImmediatePropagation()function只读取消事件的进一步捕获或者冒泡,同时阻止任何事件处理程序被调用(DOM3)

特别说明:只有在事件处理程序被执行的期间,event对象才会存在,一旦事件处理程序执行完成,其就会被销毁。

IE中的事件对象

与访问DOM中的事件对象不同,要访问IE中的event对象有几种不同的方式。取决于指定事件处理程序的方法。

  1. html event

  2. 函数参数

  3. window.event

同样IE中的event对象也包含着与创建他的事件相关的属性和方法,其中很多的属性和方法都有对应的或者是相关的DOM属性和方法。当然也会事件的不同,其属性和方法也会有所不同,但是都会包含下表内容

属性/方法类型读/写 说明
srcElementelement只读事件的目标(与DOM中的target属性相同)
typestring只读事件的类型
cancelBubbleboolean只读默认为false,设置为true表示取消冒泡(与stopPropagation()作用相同)
returnValueboolean只读默认为true,设置为false就可以取消默认行为(与preventDefault()作用相同)

跨浏览器事件对象封装

我们为eventUtil添加几个方法,以此来达到有关event对象的常用的跨浏览器的使用目标

  1. getEvent() 获取事件对象

  2. getTarget()获取事件源

  3. stopPropagation() 取消冒泡

  4. preventDefault() 阻止默认行为

   var eventUtil={
        getEvent:function(ev){
            return ev || window.event;//获取事件对象
        },
        getTarget:function(ev){
            return ev.target || ev.srcElement;//获取事件源
        },
        stopPropagation:function(ev){//阻止冒泡
            if(ev.stopPropagation){
                ev.stopPropagation();
            }else{
                ev.cancelBubble=true;
            }
        },
        preventDefault:function(ev){//阻止默认行为
            if(ev.preventDefault){
                ev.preventDefault();
            }else{
                ev.returnValue=true;
            }
        }
    }

常见应用之事件委托

说明:需要给页面中成百上千个li绑定一个事件并且输出当前元素的innerHTML
常见做法

    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>10</li>
    </ul>

    var aLi  //假设已经获取了这组li元素
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick=function(ev){
            console.log(this.innerHTML);
            
        }
    }

这种方式通过遍历DOM节点的方式添加事件处理程序有诸多缺点,比如性能大大减低,新添加的li不具备click事件等。

利用事件委托(冒泡原理)

    var oUl//假设oUl是li的父节点
    oUL.onclick=fuction(ev){
        var ev=eventUtil.getEvent(ev);
        var target=eventUtil.getTarget(ev);
            console.log(target.innerHTML);
    }

利用事件委托可以大大地提高性能,后面随时添加的元素都可以拥有这个点击事件等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值