js之DOM事件流详解

事件就是用户或浏览器自身执行的某种动作。诸如click(点击)、load(加载)、mouseover(鼠标悬停)。事件流描述的是从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。事件处理程序响应某个事件的函数就叫事件处理程序(或事件侦听器)。

DOM0级事件处理程序

下面例子中展示了dom0绑定事件的两种方法以及如何解绑事件,执行代码我们发现如果给同一元素同时绑定两个事件的话,则后面绑定的事件会把前面绑定的事件给覆盖。

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
       <button type="button" id="btn" onclick="alert('btn1')">点击按钮</button>
</body>
<script>    var btn=document.getElementById("btn");    //绑定事件    btn.onclick=function(){        alert("btn");
   };    //事件解绑    btn.onclick=null;
</script>
</html>

dom2级事件处理程序

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <meta name="viewport"          content="width=device-width, initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0"/>
   <title>Title</title>
</head>
<body>
<button type="button" id="btn" onclick="alert('btn1')">点击按钮</button>
</body>
<script>    var btn=document.getElementById("btn");    //绑定事件    btn.addEventListener("click",function(){         alert("btn");
   },false);    //事件解绑   btn.removeEventListener("click",function(){       alert("解绑");
  })
</script>
</html>

注意:addEventListener()方法第三个参数默认为false,即采用事件冒泡的形式执行程序,如果为true的话则,采用事件捕获的形式执行程序。局限于非ie浏览器,ie浏览器则采用attachEventListener绑定事件,关于ie和非ie浏览器事件绑定以及解绑,接下来我会写一个例子来展示二者之间的区别。

var eventUtil = {    //添加事件    addEventHandle:function(element,eventType,fn){        if(element.addEventListener){//非IE            element.addEventListener(eventType,fn,false);
       }else if(element.attachEvent){//IE            element.attachEvent('on'+eventType,fn);//这里拼接上'on',调运的时候不要加on,使用click等。        }else{//不支持DOM2级,使用DOM0级方式            element['on'+eventType] = fn;//这里使用[]方式实现对象的属性添加,相当于.的作用        }
   },    //删除事件    removeEventHandle:function(element,eventType,fn){        if(element.removeEventListener){//非IE,不带'on'            element.removeEventListener(eventType,fn,false);
       }else if(element.detachEvent){//IE,带'on'            element.detachEvent('on'+eventType,fn);
       }else{//不支持DOM2级,使用DOM0级方式            element['on'+eventType] = fn;
       }
   },    //获取事件对象    getEvent:function(event){        return event?event:window.event;
   },    //获取事件类型    getType:function(event){        return event.type;
   },    //获取执行事件的目标元素    getTarget:function(event){        return event.target||event.srcElement;
   },    //禁用默认行为    preventDefault:function(event){        if(event.preventDefault){            event.preventDefault();//非IE        }else{            event.returnValue = false;//针对IE        }

   },    //阻止传播冒泡    stopPropagation:function(event){        if(event.stopPrapagation){            event.stopPropagation();//非IE        }else{            event.cancelBubble = true;//针对IE        }
   }
}

dom3事件处理程序

DOM浏览器中可能发生的事件有很多种,不同事件类型具有不同的信息,DOM3级事件规定了一下几种事件:
UI事件,当用户与页面上的元素交互时触发;
焦点事件,当元素获得或者失去焦点时触发;
鼠标事件,当用户通过鼠标在页面上执行操作时触发;
滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
文本事件,当在文档中,输入文本时触发;
键盘事件,当用户通过键盘在页面上执行操作时触发;
合成事件,当为IME(Input Method Editor,输入法编辑器)输入字符时触发;
变动事件,当底层Dom结构发生变化时触发;
DOM3级事件模块在DOM2级事件的基础上重新定义了这些事件,也添加了一些新事件。包括IE9在内的主流浏览器都支持DOM2级事件,IE9也支持DOM3级事件。
DOM中的事件模拟(自定义事件):
DOM3级还定义了自定义事件,自定义事件不是由DOM原生触发的,它的目的是让开发人员创建自己的事件。要创建的自定义事件可以由createEvent("CustomEvent");
返回的对象有一个initCustomEvent()方法接收如下四个参数。
1)type:字符串,触发的事件类型,自定义。例如 “keyDown”,“selectedChange”;
2)bubble(布尔值):标示事件是否应该冒泡;
3)cancelable(布尔值):标示事件是否可以取消;
4)detail(对象):任意值,保存在event对象的detail属性中;
可以像分配其他事件一样在DOM中分派创建的自定义事件对象。如:

   
var  p = document.getElementById("myp");
EventUtil.addEventHandler(p,"myEvent", function () {
alert("p myEvent!");
});
EventUtil.addEventHandler(document,"myEvent",function(){
alert("document myEvent!");
});
if(document.implementation.hasFeature("CustomEvents","3.0")){
var e = document.createEvent("CustomEvent");
e.initCustomEvent("myEvent",true,false,"hello world!");
p.dispatchEvent(e);
}

这个例子中创建了一个冒泡事件“myEvent”。而event.detail的值被设置成了一个简单的字符串,然后在p和document上侦听该事件,因为在initCustomEvent中设置了事件冒泡。所以当p激发该事件时,浏览器会将该事件冒泡到document。
IE中的事件模拟(IE8及之前版本中):
与DOM中事件模拟的思路类似,先创建event对象,再为其指定相应信息,然后再使用该对象来触发事件。当然IE在实现以下每个步骤都不太一样。
例如:

var btn = document.getElementById("myBtn");
//创建事件对象,不接受任何参数,结果会返回一个通用的event对象,你必须为该event对象指定所有必要的信息。
var event  = document.createEventObject();
//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY =0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;

//触发事件
btn.fireEvent("onclick",event);

事件流的分类

  • 事件冒泡流:IE的事件流叫事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的节点)接收,然后逐级向上传播到较为不具体的节点

  • 事件捕获流:Netscape团队提出的另一种事件流叫事件捕获,事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件

  • dom事件流:DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段==>处于目标阶段==>事件冒泡阶段。首先发生的是事件捕获阶段,为截获事件提供了机会。然后是实际的目标接收事件。最后一个阶段是冒泡阶段。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },false);    node2.addEventListener("click",function(){        alert("node2");
    },false);    node3.addEventListener("click",function(){        alert("node3");
    },false);    node4.addEventListener("click",function(){        alert("node4");
    },false);
</script>
</html>

以上代码分别给node1-node4个节点绑定事件,且采用事件冒泡的形式执行,从而分别会弹出node4->node3->node3->node1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },true);    node2.addEventListener("click",function(){        alert("node2");
    },true);    node3.addEventListener("click",function(){        alert("node3");
    },true);    node4.addEventListener("click",function(){        alert("node4");
    },true);
</script>
</html>

以上代码分别给node1-node4个节点绑定事件,且采用事件捕获的形式执行,从而分别会弹出node1->node2->node3->node4.

那如果给同一元素同事绑定两个事件,一个采用冒泡一个采用捕获则程序会按照dom流的形式来执行程序即"先捕获后冒泡"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },false);    node2.addEventListener("click",function(){        alert("node2");
    },false);    node3.addEventListener("click",function(){        alert("node3");
    },false);    node4.addEventListener("click",function(){        alert("node4");
    },false);    
    
    node1.addEventListener("click",function(){        alert("node1");
    },true);    node2.addEventListener("click",function(){        alert("node2");
    },true);    node3.addEventListener("click",function(){        alert("node3");
    },true);    node4.addEventListener("click",function(){        alert("node4");
    },true);
    
    
</script>
</html>

程序执行顺序:node1->node2->node3->node4->node4->node3->node2->node1.

事件代理

含义:事件代理即事件委托,就是利用事件冒泡只制定一个事件处理程序就可以管理某一类型的所有事件

为什么:一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
<script>    //无事件代理    window.onload = function(){        var oUl = document.getElementById("ul1");        var aLi = oUl.getElementsByTagName('li');        for(var i=0;i<aLi.length;i++){            aLi[i].onclick = function(){                alert(123);
            }
        }
    };    //事件代理    window.onload = function(){        var oUl = document.getElementById("ul1");        oUl.onclick = function(ev){            var ev = ev || window.event;            var target = ev.target || ev.srcElement;            if(target.nodeName.toLowerCase() == 'li'){                alert(123);                alert(target.innerHTML);
            }
        }
    }
</script>
</body>
</html>

相关推荐:

js中DOM事件绑定详解

JS中DOM如何操作

js中DOM知识点分享

以上就是js之DOM事件流详解的详细内容,更多请关注智伍应用其它相关文章!