js事件原理、事件委托和事件绑定 addEventListener

前言

javascript是事件驱动型语言。当用户在网页中进行某种操作时,就产生了一个“事件”(Event)。
事件几乎可以是任何事情:单击一个网页元素、拖动鼠标等均可视为事件。JavaScript是事件驱动的,当事件发生时,它可以对之做出响应。具体如何响应某个事件由编写的事件处理函数完成。

事件基础

事件分为系统派发事件和自定义派发事件。

  • addEventListener();//事件侦听方法,仅用于EventTarget对象
  • dispatchEvent();//抛发事件方法,派发事件,仅用于EventTarget对象

注意:

  1. 必须要先侦听再派发
  2. 侦听和派发的对象是同一个,可以是DOM元素,也可以是EventTarget,或者继承EventTarget的类
  3. 侦听和派发的事件类型完全相同,其实事件类型就是一个任意字符串
  4. 系统派发的事件字符串是固定的,自定义派发的事件,字符串可以任意
  5. 事件侦听回调函数,不能传参,因此事件回调函数中有且仅有一个参数,这个参数即为事件对象
  6. 事件都可以手动派发,手动派发的话,页面加载完后就会执行
//document就是侦听派发的对象
//click就是这里的事件类型
document.addEventListener("click",clickHandler);

//div元素的继承为下:
//Object-->EventTarget-->Node-->Element-->HTMLElement-->HTMLDivElement

//EventTarget叫事件目标对象
var target=new EventTarget();
target.addEventListener("chilema",clickHandler);
//Event实例的对象叫事件对象
var evt=new Event("chilema");
evt.num=10;
target.dispatchEvent(evt);//派发事件
//
function clickHandler(e){
	//e.type为派发的事件类型
   console.log(e.type);
}

事件的高内聚低耦合

目的是使程序模块的可重用性、移植性大大增强。一个好的内聚模块应当恰好做一件事。
obj 和 obj1 互相调用,但是不相互影响,删除任意一个函数后,也不会报错。

// 高内聚低耦合
var obj={
    a:function(){
        document.addEventListener("b",this.b);
    },
    b:function(e){
        var num=e.num+10;
        var evt=new Event("a");
        evt.num=num;
        document.dispatchEvent(evt);
    }
}
var obj1={
    a:function(){
        document.addEventListener("a",this.b);
        var evt=new Event("b");
        evt.num=2;
        document.dispatchEvent(evt);
    },
    b:function(e){
        console.log(e.num);//12
    }
}
obj.a();
obj1.a();

事件原理(三个阶段)

  • 事件捕获(由外到内)
  • 目标阶段
  • 事件冒泡(由内到外)

事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。即子标签发生事件后,向父级发送该事件,一直追溯到document。如:点击一个嵌套在 body中的button,则该button的onclick事件也会传递给body、document中,触发他们的onclick里触发的函数。

理解事件原理:
事件冒泡

var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
var div3 = document.getElementById("div3");
div1.addEventListener("click", clickHandler1);
div2.addEventListener("click", clickHandler2);
div3.addEventListener("click", clickHandler3);
function clickHandler1(e) {
    console.log("div1")
}
function clickHandler2(e) {
    console.log("div2")
}
function clickHandler3(e) {
    console.log("div3")
}

阻止事件冒泡

当对子元素添加了事件侦听后,执行的时候会触发父元素相同类型的事件,此时需要阻止事件冒泡。
早期IE是没有捕获阶段的,只有冒泡,cancelBubble为阻止冒泡。后来的stopPropagaiton,既有阻止冒泡的功能,也有阻止捕获的功能,但如果译为阻止传播,那么跟cancel就是两个东西了,所以还是叫做阻止冒泡。阻止事件冒泡(传播)的方法是:

  • e.stopPropagation();
  • e.cancelBubble=true; IE8及以下浏览器
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
var div3 = document.getElementById("div3");
div1.addEventListener("click", clickHandler1);
div2.addEventListener("click", clickHandler2);
div3.addEventListener("click", clickHandler3);
function clickHandler1(e) {
	e.stopPropagation();//阻止事件冒泡
	// e.cancelBubble=true;//阻止事件冒泡   IE8一下浏览器
    console.log("div1")
}
function clickHandler2(e) {
    console.log("div2")
}
function clickHandler3(e) {
    console.log("div3")
}

事件委托

事件侦听添加(注册事件)占有内存的,尽量减少事件侦听的数量,将子元素的事件委托给父元素来执行,叫做事件委托。
当删除对象时,一定要将对象上的侦听事件移除,否则会造成内存泄露。

  • e.currentTarget 是事件侦听事件对象(什么对象执行addEventListener函数就是谁)
  • e.target 事件的目标对象 事件实际触发的目标阶段最后对象,
  • e.srcElement 事件的目标对象,兼容IE
  • 事件函数中this默认等同于e.currentTarget

案例:点击 li ,让其子元素 ul 切换显示。
js事件委托
布局代码:

<ul id="menu">
   <li>北京
        <ul>
            <li>海淀</li>
            <li>昌平
                <ul>
                    <li>沙河</li>
                    <li>回龙观</li>
                    <li>天通苑</li>
                </ul>
            </li>
            <li>朝阳</li>
            <li>东城</li>
        </ul>
    </li>
    <li>河北</li>
    <li>天津</li>
    <li>河南</li>
    <li>山西</li>
</ul>

js代码:

var menu=document.querySelector("#menu");
//给最外层的ul侦听点击事件
menu.addEventListener("click",clickHandler);

function clickHandler(e){
	//如果点击的不是li标签,直接退出
    if(e.target.constructor!==HTMLLIElement)return;
    //如果点击的li没有子元素,直接退出
    if(!e.target.firstElementChild) return;
    //阻止事件冒泡
    e.stopPropagation();
    //控制li下的ul显示隐藏
    if(!e.target.bool) e.target.firstElementChild.style.display="none";
    else e.target.firstElementChild.style.display="block";
    e.target.bool=!e.target.bool;
}

扩展-addEventListener()

  • 参数:addEventListener(事件类型,事件回调函数,是否捕获阶段执行(默认false)||对象{once:true})//是否只执行一次
//给body侦听点击事件,冒泡阶段执行
 document.body.addEventListener("click", clickHandler);
 //给body侦听点击事件,捕获阶段执行
 document.body.addEventListener("click", clickHandler,true);
 //给body侦听点击事件,只执行一次
 document.body.addEventListener("click", clickHandler,{once:true});
  • 事件侦听添加(注册事件)占有内存的,所以当删除对象时,一定要将对象上的侦听事件移除
 document.body.removeEventListener("click", clickHandler);

addEventListener() 和 onclick的区别:

  • onclick 不能同时执行两个函数,addEventListener()可以执行两个不同的函数
  • 移除事件侦听的方式不同
document.onclick=function(){
	document.onclick=null;//移除事件侦听
    console.log("a")
}
//事件会覆盖上面的事件
document.onclick=function(){
    console.log("b");
}

document.addEventListener("click",clickHandler1);
document.addEventListener("click",clickHandler2);
function clickHandler1(e){
	//移除事件侦听
	document.removeEventListener("click",clickHandler1);
    console.log("a");
}
function clickHandler2(e){
   console.log("b")
}
  • 事件匿名函数的不断迭代就会造成回调地狱,而使用事件注册时用的是命名函数就会减少造成回调地狱
// 事件匿名函数的不断迭代就会造成回调地狱
document.onclick=function(){
    var bn=document.querySelector("button");
    bn.onclick=function(){
        console.log("aaa");
    }
}
  • addEventListener可以在捕获阶段和冒泡阶段触发,而onclick只能冒泡阶段触发
 document.body.addEventListener("click", clickHandler);//冒泡阶段执行
 document.body.addEventListener("click", clickHandler,true);//捕获阶段执行
  • onclick支持IE低版本,addEventListener不支持IE8一下,低版本的IE使用 attachEvent 进行事件侦听;使用 detachEvent 移除事件侦听。
document.attachEvent("onclick",clickHandler);//IE8及以下使用,其他版本和其他浏览器不支持
function clickHandler(e){
    console.log("aaa");
    document.detachEvent("onclick",clickHandler);//移除事件侦听
}

事件侦听和移除的兼容写法:

function addEvent(elem,type,eventHandler){
    try{
        elem.addEventListener(type,eventHandler);
    }catch(e){
        elem.attachEvent("on"+type,eventHandler);
    }
}

function removeEvent(elem,type,eventHandler){
    try{
        elem.removeEventListener(type,eventHandler);
    }catch(e){
        elem.detachEvent("on"+type,eventHandler);
    }
}

addEvent(document,"click",clickHandler);
function clickHandler(){

}
发布了46 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Charissa2017/article/details/103855079