JavaScript 详说事件流(冒泡、捕获、传播、委托)

事件流

事件流描述的是从页面中接收事件的顺序。
标准DOM事件流存在三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

事件捕获:当触发事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素也绑定了对应事件的话,会先触发父元素绑定的事件。
事件冒泡:与事件捕获恰恰相反,事件冒泡的顺序是由内到外进行事件传播,直到根节点。
无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播。

注意:IE低版本不支持事件捕获,所以就少了一个事件捕获阶段,其他浏览器则同时存在。

事件委托:由于事件冒泡的存在,可以把原本想要绑定到子元素上的事件绑定到父元素上,满足条件时触发事件。事件委托用到了event对象的target(触发事件的某个具体对象)属性和currentTarget(绑定事件的对象)属性来区分父元素和子元素。

如何绑定事件

方法一:直接在标签中添加事件属性,事件前面要加on
缺点:HTML与JS耦合,不利于维护。还有就是可能存在元素已经渲染但事件代码还未加载,这种情况下触发事件会报错。

    <!-- 直接在onclick定义代码 -->
    <div onclick="alert('hello')">click Me</div>

    <!-- 触发一个函数 -->
    <div onclick="alertHello()">click Me</div>
    <script>
        function alertHello() {
            alert('hello');
        }
    </script>

方法二:是将一个函数赋值给DOM节点的事件属性,事件前面要加on
特点:事件处理程序是在元素的作用域中运行,程序中的 this 引用当前元素。

    <div id="div">click me</div>
    <script>
        var div = document.getElementById('div');
        div.onclick = function () {
            alert('hello');
        }
        
        //可以通过下面这行代码删除事件
        //div.onclick = null;
    </script>

方法三:addEventListener() 接受三个参数:要处理的事件名(不用加on)、事件处理函数和一个布尔值(可选,默认值为false,表示事件在冒泡阶段执行,true表示事件在捕获阶段执行)。
缺点:IE低版本浏览器不支持。
特点:为同一事件定义多个事件处理程序,触发事件时会以添加它们的顺序执行。事件处理程序是在元素的作用域中运行,程序中的 this 引用当前元素。

    <div id="div">click me</div>
    <script>
        var div = document.getElementById('div');
        function alertHello() {
            alert('hello');
        }
        div.addEventListener('click', alertHello, false);
        
        // 可以通过下面这行代码删除事件
        // div.removeEventListener('click', alertHello, false);
        // 如果绑定的事件函数是匿名函数,如
        // div.addEventListener('click', function(){}, false);
        // 则无法删除事件
    </script>

方法四:attachEvent()接受两个参数:事件名(要加on)与事件处理函数。由于 IE8 及更早版本只支持事件冒泡,所以通过添加的事件处理程序都会被添加到冒泡阶段。
缺点:只能在IE8及更早版本使用。
特点:事件处理程序会在全局作用域中运行,因此 this 等于 window。为同一事件定义多个事件处理程序,触发事件时不会以添加它们的顺序执行,而是以相反的顺序被触发。

    <div id="div">click me</div>
    <script>
        var div = document.getElementById('div');
        function alertHello() {
            alert('hello');
        }
        div.attachEvent("onclick", alertHello);
        
        // 可以通过下面这行代码删除事件
        // div.detachEvent('click', alertHello, false);
        // 如果绑定的事件函数是匿名函数,如
        // div.attachEvent('click', function(){}, false);
        // 则无法删除事件
    </script>
阻止事件冒泡

方法一(推荐):event.stopPropagation();
缺点:IE浏览器无法使用。

    <div id="parent">
        <div id="child">click me</div>
    </div>
    
    <script>
        var parent = document.getElementById('parent'),
            child = document.getElementById('child');
        parent.addEventListener('click', function() {
            alert('parent');
        })
        child.addEventListener('click', function(e) {
            alert('child');
            e.stopPropagation();
        })
    </script>

方法二:event.cancelBubble = true;
缺点:只能在IE浏览器使用。

    <div id="parent">
        <div id="child">click me</div>
    </div>
    
    <script>
        var parent = document.getElementById('parent'),
            child = document.getElementById('child');
        parent.attachEvent('onclick', function() {
            alert('parent');
        })
        child.attachEvent('onclick', function() {
            alert('child');
            window.event.cancelBubble = true;
        })
    </script>

方法三:event.stopImmediatePropagation()
特点:阻止事件冒泡并且阻止相同事件的其他侦听器被调用。

    <div id="parent">
        <div id="child">click me</div>
    </div>
    <script>
        var parent = document.getElementById('parent'),
            child = document.getElementById('child');
        parent.addEventListener('click', function () {
            alert('parent');
        })
        child.addEventListener('click', function (e) {
            alert('child');
            e.stopImmediatePropagation();
        })
        child.addEventListener('click', function (e) {
            alert('hello');     //不会执行
        })
    </script>
取消默认事件

方法一(推荐):event.preventDefault()
缺点:IE8及以下版本浏览器不支持。

    <a href="https://www.baidu.com" onclick="alert('hello');event.preventDefault();">click me</a>

方法二:event.returnValue = false;
缺点:非标准,兼容性差。

    <a href="https://www.baidu.com" onclick="alert('hello');event.returnValue=false;">click me</a>

方法三:return false;
缺点:在addEventListener注册的事件处理函数中无法阻止事件默认行为。

    <a href="https://www.baidu.com" onclick="alert('hello');return false;">click me</a>
事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。这个 event 对象会传入到事件处理函数中。

dom.onclick = function (e) {
    alert(e.type)
}

直接定义在元素上的事件中的变量 event 中保存着 event 对象。

<button onclick="alert(event.type)">click me</button>
属性类型读写描述
targetElement只读事件的目标
currentTargetElement只读事件处理函数所在的那个元素
stopPropagation()Function只读取消事件的进一步捕获或冒泡。如果bubblestrue,则可以使用这个方法
stopImmediatePropagation()Function只读取消事件的进一步捕获或冒泡,同时阻止任何相同事件处理程序被调用
bubblesBoolean只读表明事件是否冒泡
preventDefault()Function只读取消事件的默认行为。如果cancelabletrue,则可以使用这个方法
cancelableBoolean只读表明是否可以取消事件的默认行为
defaultPreventedBoolean只读true 表示已经调用了 preventDefault()
typeString只读被触发的事件的类型
eventPhaseInteger只读调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段
trustedBoolean只读true表示事件是浏览器生成的。为false表示事件是由开发人员通过 JavaScript 创建的
detailInteger只读与事件相关的细节信息
viewAbstractView只读与事件关联的抽象视图。等同于发生事件的window对象

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

    <div id="parent">
        <div id="child">
            clickme
        </div>
    </div>
    <script>
        let parent = document.getElementById("parent"),
            child = document.getElementById("child");
        parent.addEventListener('click', function(e){
            console.log(this === parent);   //true
            console.log(e.target === child);    //true
            console.log(e.currentTarget === parent);    //true
        });
    </script>
IE事件对象

直接定义DOM对象的事件属性,event 对象会作为 window 对象的一个属性存在。

dom.onclick = function(){
    console.log(window.event.type);
}

如果事件处理程序是使用 attachEvent() 添加的,那么就会有一个 event 对象作为参数被传入事件处理程序函数中。

dom.attachEvent("onclick", function (event) {
    alert(event.type); 
});

直接定义在元素上的事件中的变量 event 中保存着 event 对象。

<button onclick="alert(event.type)">click me</button>
属性类型读写描述
cancelBubbleBoolean读/写默认值为false,但将其设置为true就可以取消事件冒泡
returnValueBoolean读/写默认值为true,但将其设置为false就可以取消事件的默认行为
srcElementElement只读事件的目标
typeString只读被触发的事件的类型

因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为 this 会始终等于事件目标。故而,最好还是使用 event.srcElement 比较保险。例如:

    <div id="parent">
        clickme
    </div>
    <script>
        var div = document.getElementById("parent");
        div.onclick = function () {
            alert(window.event.srcElement === this);    //true 
        };
        div.attachEvent("onclick", function (event) {
            alert(event.srcElement === this);   //false 
            alert(this === window); //true 
        });
    </script>

由于 IE 不支持事件捕获,因而只能取消事件冒泡;但 stopPropagatioin()可以同时取消事件捕获和冒泡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值