理解DOM中的事件流的概念

13 篇文章 0 订阅

原文地址


什么是事件流

:用术语说流是对输入输出设备的抽象。以程序的角度说,流是具有方向的数据。

事件流:从页面中接收事件的顺序。也就是说当一个事件产生时,这个事件的传播过程,就是事件流。

事件:用户或者浏览器自身执行的某个动作,比如load,click,mousemove等

事件处理程序:相应处理某个事件的函数叫做事件处理函数(也叫做事件侦听器)

比如说React中的单向数据流,Node中的流,又或是今天本文所讲的DOM事件流。都是流的一种生动体现。


理解DOM中的事件流

当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆。两家公司的开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,那么同时你也单击了按钮的容器元素,甚至整个页面。
事件流描述的是从页面中接受事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了两个截然相反的事件流概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。


IE提出的事件冒泡

事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button id="click">点击</button>
    <script>
        (function(){
            var btn = document.getElementById("click");
            btn.onclick = function(){
                console.log("1. button");
            }
            document.body.onclick = function(){
                console.log("2. document.body");
            }
            document.onclick = function(){
                console.log("3. document");
            }
            window.onclick = function(){
                console.log("4. window");
            }
        })()
    </script>
</body>
</html>

在代码所示的页面中,如果点击了button,那么这个点击事件会得到如下的结果:
代码结果截图
也就是说,click事件首先在button元素上发生,然后逐级向上传播。这就是事件冒泡。


netscape提出的事件捕获

事件捕获的概念,与事件冒泡正好相反。它认为当某个事件发生时,父元素应该更早接收到事件,具体元素则最后接收到事件。比如说刚才的demo,如果是事件捕获的话,事件发生顺序会是刚好与上面相反的。即window,document,document.body,button。

虽然事件捕获是Netscape唯一支持的事件流模型,但IE9、Safari、Chrome、Opera和Firefox目前也都支持这种事件流模型。但由于老版本的浏览器不支持,因此很少有人使用事件捕获。

所以放心的使用事件冒泡,有特殊需要再使用事件捕获即可。


DOM事件流

DOM事件流可以分为下面3个阶段:
1. 事件捕获阶段
2. 处于目标阶段
3. 事件冒泡阶段
dom事件


事件捕获阶段

也就是说,当事件发生时,首先发生的是事件捕获,为父元素截获事件提供了机会。
例如,我把上面的Demo中,window点击事件更改为使用事件捕获模式。

addEventListener最后一个参数,为true则代表使用事件捕获模式,false则表示使用事件冒泡模式。

<script>
    (function(){
        var btn = document.getElementById("click");
        btn.addEventListener("click",function(){
            console.log("1. button");
        },true)
        //省略document.body和document
       .....
        window.addEventListener("click",function(){
            console.log("4. window");
        },true)
    })()
    </script>

结果如下:

可以看到,点击事件先被父元素截获了,且该函数只在事件捕获阶段起作用。

在DOM事件流中,事件的目标在捕获阶段不会接受到事件。这意味着在捕获阶段,事件从document到body后就定停止了。下一个阶段是处于目标阶段,于是事件在button上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回document。

但是:我们的各大浏览器总是不喜欢按照规范来,IE9,Safari,chrome,firefox及其更高的版本中都会在捕获阶段出发事件对象上的事件,最后导致有两个机会在目标对象上操作事件。


处于目标与事件冒泡阶段

事件到了具体元素时,在具体元素上发生,并且被看成冒泡阶段的一部分。
随后,冒泡阶段发生,事件开始冒泡。


阻止事件冒泡

件冒泡过程,是可以被阻止的。防止事件冒泡而带来不必要的错误和困扰。

这个方法就是:stopPropagation()

(function(){
    var btn = document.getElementById("click");
    btn.addEventListener("click",function(event){
        console.log("1. button");
        event.stopPropagation();
        console.log('Stop Propagation!');
    },false)
    //省略document.body和document
       .....
    window.addEventListener("click",function(){
        console.log("4. window");
    },false)
})()

最后结果是:1.button,Stop Propagation!。通过stopPropagation();阻止了事件的冒泡。


事件处理程序类别

刚刚我们已经讲了事件处理程序就是相应处理某个事假的函数。它可以分为几个类别:

html事件处理程序

某个元素支持的某个事件可以用与事件处理程序同名的html特性来指定,该特性的值是能够执行的javascript代码,这也是我们最初学js,最开始的方法。

<script>
    function show(){
        alert('我被点击了');
    }
/*
  点击后也会弹出 '我被点击了'
*/
</script>
<input type="button" value="点击" onclick="show()" />

优点:简单明了,省去获取元素等一系列前提操作

缺点:html代码与js代码高度耦合,不符合分离原则


DOM0级别事件处理函数

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>

DOM2级事件处理程序

DOM2级添加了addEventListener(添加事件处理程序)和removeEventListener(移除事件处理程序),也就是我们刚刚讲的上面的DOM2例子。

添加事件处理函数addEventListener

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

移除事件处理函数removeEventListener

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

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

最后只能弹出HellowWorld

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

 oBtn.attachEvent('onclick',showId);//这时候会报错,因为这里的是在window的作用域内
//修改如下
oBtn.detachEvent('onclick',showId) ;//点击没有任何反应
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值