【事件绑定的方式】兼容浏览器的事件监听器的设计

本文主要探讨了兼容IE8以前浏览器的事件监听器设计,详细讲解了事件绑定的三种方式,强调了事件对象获取和事件处理函数的绑定特性。还深入解析了事件的捕获和冒泡阶段,包括事件捕获的概念和事件冒泡的执行顺序。最后,文章通过实例说明了如何利用事件冒泡实现事件代理,以减少代码冗余并提高可读性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、兼容浏览器的事件监听器
主要是兼容IE8以前的浏览器,addEvent方法中介绍了三种绑定事件的方式,其中要注意:
(1)在IE的事件绑定方式中,事件处理函数里面的事件对象时挂在window上面的,所以获取event对象时需要利用window.event来获取
(2)addEventListener添加监听器时注意事件的冒泡和事件的捕获,即该方法的第三个参数,事件的冒泡和捕获注意的事项参见下文
(3)利用属性绑定事件只能为同一个类型的事件添加一个事件处理函数,其他的两种方式均可以为同一类型的事件添加多个处理函数。所以属性绑定事件移除时直接赋值null即可。

EventUnit={
    addEvent:function(ele,type,handler){
        if(ele.addEventListener)
        {
            ele.addEventListener(type,handler,false);
        }
        else if(ele.attachEvent)
        {
            ele.attachEvent('on'+type,handler);
        }
        else ele['on'+type]=handler;
    },
    removeEvent:function (ele,type,handler) {
        if(ele.removeEventListener)
            ele.removeEventListener(type,handler);
        else if(ele.dispatchEvent())
            ele.dispatchEvent('on'+type,handler);
        else ele['on'+type]=null;
    },
    stopPropagation:function (ev) {
        if(ev.stopPropagation) ev.stopPropagation();
        else ev.cancelBubble=true;
    },
    preventDefault:function (ev) {
        if(ev.preventDefault)ev.preventDefault();
        else ev.returnValue=false;
    },
    getTarget:function(ev)
    {
       return ev.target||ev.srcElement;
    },
    getEvent:function (ev) {
        var ev=ev||window.event;
        return ev;

    }

}

二、事件的捕获和冒泡
1、基本概念
(1)在IE8+的浏览器以及谷歌等浏览器中事件的发生均有三个阶段:事件的捕获–》事件触发对象–》事件冒泡
(2)事件捕获:
当event.target触发事件时,事件首先会发生在捕获阶段,即从上到下执行阶段,当该元素的祖先元素注册了同类型的事件且事件时发生捕获阶段(addEventListener的第三个参数设置为true,默认情况下所有事件时发生在冒泡阶段的,除了不能发生冒泡的事件)时,会先触发祖先元素的事件函数,一次向下执行。
(3)事件的冒泡则与事件的捕获的发生过程想法,事件的执行过程时从event.target向上冒泡执行的,不能冒泡的事件有:mouseenter/mouseleave/focus/blur/load等
2、事件冒泡捕获的具体情况
在同一个元素上注册同一类型事件的两种处理方式的回调函数,即既有冒泡的回调也有捕获的回调

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #one{
            width:400px;
            height:400px;
            border:2px solid red;
        }
        #two{
            width:300px;
            height:300px;
            border:2px solid blue;
        }
        #three{
            width:200px;
            height:200px;
            border:2px solid chartreuse;
        }
        #four{
            width:100px;
            height:100px;
            border:2px solid rosybrown;
        }
    </style>
</head>
<body>
<div id='one'>
    <div id='two'>
        <div id='three'>
            <div id='four'>
            </div>
        </div>
    </div>
</div>

<script type='text/javascript'>
    var one=document.getElementById('one');
    var two=document.getElementById('two');
    var three=document.getElementById('three');
    var four=document.getElementById('four');

    one.addEventListener('click',function(){
        alert('one,bubble');
    },false);
      one.addEventListener('click',function(){
        alert('one,capture');
    },true);
    two.addEventListener('click',function(){
        alert('two,bubble');
    },false);
    two.addEventListener('click',function(){
        alert('two,capture');
    },true);
    three.addEventListener('click',function(){
        alert('three,capture');
    },true);
    four.addEventListener('click',function(){
        alert('four');
    },true);
</script>
</body>
</html>

(1)点击元素只注册一种类型事件(捕获或者冒泡)
如果点击的是元素three,则执行的结果是:one,capture –> two,capture –> three,bubble–> two ,bubble–> one ,bubble
由此可以看出事件是严格遵循事件的处理的三个阶段的
(2)当点击的元素本身既具有捕获和冒泡事件时(点击two)
程序的执行结果是:one,capture –> two,bubble –> two,capture–>one,bubble
是不是看到这样的执行结果你觉得很奇怪,为什么two,bubble会先于two,capture呢?原因是在当前点击的元素上如果同时注册有捕获和冒泡事件其执行顺序是按照注册的先后顺序,先注册,先执行。所以才出现了这样的结果,你可以交换two事件的注册顺序后,继续点击two看看效果。当你点击one时就会发现执行的顺序也是按照注册的顺序执行的。

3、利用事件冒泡实现事件代理
同一类型的事件当在对个子元素上进行添加绑定时,会添加代码的冗余度,同时使得代码的可读性变差,因而可以利用时间的冒泡的方式将该事件绑定在其公共的父元素上,减少代码,同时当动态添加子元素时,也可以为该元素绑定上事件,如果是采用原始的方式,则动态添加的元素上将不会有时间处理函数。
在下面的代码中可以做测试,放开注释的部分既可以看到效果,如果要对比可以参见我的另一篇博客阿里前端内推笔试题

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <!--code here-->
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, minimal-ui">
    <meta name="format-detection" content="telephone=no">
    <title>demo</title>
    <style>
        * { padding: 0; margin: 0;}
        .record-head{width:100%}
        .head, li div { display: inline-block; width: 20%; text-align: center; }
        li .id, li .sex, .id, .sex { width: 10%; }
        li .name, .name { width: 10%; }
        li .tel, .tel { width: 20%; }
        .del,.user-delete { width:10%; }
        ul { list-style: none; }
        .user-delete { cursor: pointer; }
    </style>
</head>

<body>
<div id="J_container">
    <div class="record-head">
        <div class="head id">序号</div>
        <div class="head name">姓名</div>
        <div class="head sex">性别</div>
        <div class="head tel">电话号码</div>
        <div class="head province">省份</div>
        <div class="head del">操作</div>
    </div>
    <ul id="J_List">
        <li>
            <div class="id">1</div>
            <div class="name">张三</div>
            <div class="sex"></div>
            <div class="tel">13788888888</div>
            <div class="province">浙江</div>
            <div class="user-delete">删除</div>
        </li>
        <li>
            <div class="id">2</div>
            <div class="name">李四</div>
            <div class="sex"></div>
            <div class="tel">13788887777</div>
            <div class="province">四川</div>
            <div class="user-delete">删除</div>
        </li>
        <li>
            <div class="id">3</div>
            <div class="name">王二</div>
            <div class="sex"></div>
            <div class="tel">13788889999</div>
            <div class="province">广东</div>
            <div class="user-delete">删除</div>
        </li>
    </ul>
</div>

<script>
    class Concat{
        constructor(){
            this.init();
        }
        init(){
            let Ul = document.querySelector("#J_List");
            /*利用代理模式绑定事件,一则可以避免使用循环绑定,
            二对于动态添加元素也可以触发事件,
            但是有个问题是点击ol其他的位置可能会导致事件的响应,所以可以根据功能进行权衡,选择哪一种方式*/
           Ul.addEventListener("click",(e) => {
               Ul.removeChild(e.target.parentNode);
           },false)
        }
    }
    new Concat();
    /*测试用的,动态添加事件*/
//  let  li = document.createElement("li");
//  li.innerHTML = ` <div class="id">3</div>
//            <div class="name">三二</div>
//            <div class="sex">男</div>
//            <div class="tel">13788889999</div>
//            <div class="province">广东</div>
//            <div class="user-delete">删除</div>`;
//  let ul = document.querySelector("#J_List");
//  ul.appendChild(li);
</script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值