0


Javascript事件的深度解析与代码实战(监控&冒泡&触发&传播)

文章目录

1. js代码书写位置&事件初识&文档加载

  • js书写位置共四种,下面先来看前三种:- 第一种方式:head中使用script标签,令type属性为"text/javascript",不写type默认也是"text/javascript"- 第二种方式:将js代码写到标签的属性中,如onlick属性,当我们点击按钮时,js代码才会执行- 第三种方式:head中使用script标签,指明src引入一个外部的js文件
  • 在讲解第四种书写位置时,先来看事件和文档加载:- 事件:- 定义:就是用户和浏览器之间的交互行为,比如:点击按钮,鼠标移动、关闭窗口- 我们可以在事件对应的属性中设置一些js代码,这样当事件被触发时,这些代码将会执行。即js的第二种书写位置,如在onclik属性中添加js代码,当点击时,执行js。种写法我们称为结构和行为耦合,不方便维护,不推荐使用<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><buttonid="btn"onclick="alert('提交成功')"> 提交 </button></body></html>

在这里插入图片描述

  • ps:第一种和第三种方式也可以给js绑定事件,还是上例,如果通过在script标签中使用js代码绑定事件,首先要找到button按钮,然后为他绑定onclick事件,如下代码(下例中,onclik事件后面为function函数,称为回调函数,不由我们调用,当触发onclik事件时,自动调用),最终页面显示效果是:点击提交按钮后,没有反应(未弹出提交成功的弹窗),具体原因文档加载会讲
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script>// 1. 首先找到button按钮:通过id号查找 // getElementByID:通过id号找到元素,后续会详细介绍,这样我们就找到了button按钮var btn = document.getElementById("btn");// 2. 为button按钮绑定onclick事件:一旦点击按钮,就相当于调用了该函数
            btn.onclick=function(){alert("提交成功");};</script></head><body><buttonid="btn">
            提交
        </button></body></html>
  • 文档加载:- 浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行。- 如果将script标签写到页面的上边,无论是直接在script中书写js(第一种书写位置)还是在script中通过src引入外部js文件(第三种书写位置),都会先执行script中的js代码,此时页面还没有加载,即DOM对象还没有加载,因此无法获取DOM对象(上例中的button对象就无法获取)。- 上例中button按钮的onclick事件会在加载完button后点击触发,由于js代码写到script标签中,先执行script(页面还没有加载,DOM对象还没有加载),js代码无法获取到button元素,最终导致onload函数不起作用。
  • 既然我们不推荐第二种方式,第一种和第三种方式又存在弊端,故引出第四种js书写位置:将script标签及里面的js代码写到body中对应元素的下方
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><buttonid="btn">
        提交
    </button><!-- 直接在button后面写js代码,这样button已经加载完成 --><script>var btn = document.getElementById("btn");  
        btn.onclick=function(){alert("提交成功");};</script></body></html>

2. 再会js中的事件

  • js语言本身不会产生事件,产生事件的是网页文档和网页文档中的html元素。事件就是网页文档或网页元素对外发出的通知。

  • html事件发生在html元素上,如上例中button按钮的onclick。html事件可以是浏览器行为,也可以是用户行为,如下html事件的实例:- html页面完成加载(浏览器行为)- html input字段改变时(浏览器行为)- html按钮被点击(用户行为)

  • 通常,当事件发生时,可以触发js代码。如果js需要对DOM对象触发的事件进行处理,就需要监听事件,浏览器会自动调用监听事件的js函数,js监听事件的方式有三种:- 内联属性监听:<button id="btn" onclick="alert('提交成功')">提交</button>(已接触)- DOM属性绑定监听:即上例btn.onclick=function(){xxx}(已接触)- 使用事件监听函数:对HTML元素对象调用addEventListener(event,function,useCapture)方法或attachEvent()方法,绑定事件的处理函数(后续讲解)

  • 常见的html事件如下:(各种事件可以在后续学习中慢慢体会,下表仅是部分事件)
    事件描述onchangehtml元素改变onclick用户点击html元素onmousemove鼠标在元素中移动时发生onmouseover鼠标指针移动到指定的元素上时发生onmousedown当鼠标在元素上按下时发生onmouseup当鼠标按键在元素上松开时发生onmouseout用户从一个html元素上移开鼠标时发生onkeydown用户按下键盘按键onload浏览器已完成页面的加载

  • onload是浏览器完成页面加载后自动触发,因此上例中的问题,除了使用js第四种书写位置,还可以通过在head的script标签中为元素绑定onload事件解决

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script>// onload事件会在整个页面加载完成之后才触发,为window绑定一个onload事件// 该事件对应的响应函数将会在页面加载完成之后执行,这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了
        window.onload=function(){//获取id为btn的按钮var btn = document.getElementById("btn");//为按钮绑定一个单击响应函数
            btn.onclick=function(){alert("hello");};};</script></head><body><buttonid="btn">提交</button></body></html>

3. 深入理解事件

  • 关于事件,在前面章节我们已经有了初步的接触,事件指的就是用户与浏览器交互的一瞬间。我们通过为指定事件绑定回调函数的形式来处理事件,当指定事件触发以后,我们的回调函数就会被调用,这样我们的页面就可以完成和用户的交互了。

3.1 事件触发和事件对象

  • 事件触发: - 事件的发生(触发)主要是由用户操作引起的。- 如mousemove这个事件就是由于用户移动鼠标引起的,在鼠标指针移动的过程中该事件会持续发生- 当指定事件被触发时,浏览器就会调用对应的函数(代码)去响应事件。一般情况下,事件每触发一次,函数就会执行一次。因此,设置鼠标移动的事件可能会影响到鼠标的移动速度,所以设置该类事件时一定要谨慎。
  • 事件对象: - 当事件被触发时,就会调用响应函数,在DOM标准的浏览器中,浏览器每次都会将一个事件对象作为实参传递进响应函数。在事件对象中封装了当前事件相关的一切信息,比如鼠标的坐标,键盘哪个按键被按下,鼠标滚轮滚动的方向等等。- IE中的事件对象: - IE8以上的浏览器和标准浏览器一样- 在IE8及以下的浏览器中,响应函数被触发时,浏览器不会传递事件对象,而是将事件对象作为window对象的属性保存。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><styletype="text/css">#areaDiv{border: 1px solid black;width: 300px;height: 50px;margin-bottom: 10px;}#showMsg{border: 1px solid black;width: 300px;height: 20px;}</style><script>
        window.onload=function(){/*
            * 当鼠标在areaDiv中移动时,在showMsg中来显示鼠标的坐标
            *///获取两个divvar areaDiv = document.getElementById("areaDiv");var showMsg = document.getElementById("showMsg");// 为areaDiv绑定onmousemove事件
            areaDiv.onmousemove=function(event){// 参数event写不写都行,详情见js语法-函数/*
                * 在IE8中,响应函数被处罚时,浏览器不会传递事件对象,
                *     在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
                *//*if(!event){
                    event = window.event;
                }*///解决事件对象的兼容性问题
                event = event || window.event;/*
                * clientX可以获取鼠标指针的水平坐标
                * cilentY可以获取鼠标指针的垂直坐标
                */var x = event.clientX;var y = event.clientY;//在showMsg中显示鼠标的坐标
                showMsg.innerHTML ="x = "+x +" , y = "+y;};};</script></head><body><divid="areaDiv"></div><divid="showMsg"></div></body></html>

在这里插入图片描述

  • 事件对象event包含与创建它的特定事件有关的属性和方法,触发的事件类型不一样,可用的属性和方法也不一样。下图第一张图片为标准浏览器一些公共方法或属性,第二张图片为IE8及以下浏览器一些公共方法或属性。

在这里插入图片描述
在这里插入图片描述

  • 下述案例为让id为box1的div跟随鼠标移动而移动:- 设置body的width和height为一个相对较大的值,可以出现水平和垂直方向滚动条- 设置box1开启绝对定位- 对于浏览器滚动条,chrome认为浏览器滚动条是body元素的,可以通过document.body.scrollTop来获取;火狐等浏览器认为滚动条是html的,通过document.documentElement.srollLeft获取- clientXclientY用于获取onmousemove事件中鼠标在当前的可见窗口的坐标- pageXpageY用于获取onmousemove事件中鼠标在当前页面的坐标,但是IE8及以下浏览器不兼容。- box1绝对定位的偏移量为可见窗口坐标加上滚动条偏移量<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text/css">#box1{width: 100px;height: 100px;background-color: red;/* 为box1开启绝对定位 */position: absolute;}</style><scripttype="text/javascript"> window.onload=function(){/* * 使div(box1)可以跟随鼠标移动 */// 获取box1var box1 = document.getElementById("box1");// 为document绑定鼠标移动事件 document.onmousemove=function(event){// 获取滚动条滚动的距离:解决chrome和其他浏览器的兼容问题var st = document.body.scrollTop || document.documentElement.scrollTop;var sl = document.body.scrollLeft || document.documentElement.scrollLeft;//var st = document.documentElement.scrollTop;// 获取event:解决兼容问题 event = event || window.event;// 获取到鼠标在可见窗口的坐标var left = event.clientX;var top = event.clientY;//设置div的偏移量 box1.style.left = left + sl +"px"; box1.style.top = top + st +"px";};};</script></head><bodystyle="height: 1000px;width: 2000px;"><!-- 设置box1跟随鼠标移动而移动 --><divid="box1"></div></body></html>

在这里插入图片描述

3.2 事件的绑定(事件监控)

  • 在第三章中,我们已经提到了事件绑定的三种方式:- 方式一:内联属性监听,也可以称为通过HTML元素指定事件属性来绑定- 方式二:DOM属性绑定监听:通过DOM对象指定的属性来绑定- 方式三:设置事件监听器(事件监听函数),使用元素对象.addEventListener()或者元素对象.attachEvent()方法。
  • 前两种事件绑定方式的比较- 方式一:举例<button onclick="alert('hello');alert('world');">按钮</button>,这种方式,当我们点击按钮以后,onclik属性中的js代码将会依次执行,也就是点击按钮后,页面会弹出两个提示框。除了直接将代码编写到onclik属性中,也可以事先在外部定义好函数。- 优点:设定步骤非常简单,并且能够确保事件处理程序在载入时被设定。- 缺点:将js和html代码编写到一起,并不推荐;只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的(在方式二讲解)- 方式二:举例如下,效果同方式一。var btn = document.getElementById("btn");btn.onclik=function(){alert("hello");alert("world");}- 优点:html代码和js代码写在不同位置,维护起来更加容易。- 缺点:只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的。如下例,同一个btn元素的onclick事件绑定了两个响应函数,则第二个会覆盖第一个,即结果只有一个提示框,内容为"hello javascript"("hello"和"world"提示框内容不会生效)var btn = document.getElementById("btn");btn.onclik=function(){alert("hello");alert("world");}btn.onclick=function(){alert("hello javascript");}- 方式一和方式二我们在前述章节都有所接触,这两种方式都是日常用的比较多的,但更推荐使用第二种方式。两种方式都存在一个缺点:只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,可以使用方式三来解决这个问题。
  • 方式三:设置事件监听器详解:- IE8以上浏览器及其他非IE浏览器:使用元素.addEventListner(参数1, 参数2, 参数3)可以同时为一个元素的相同事件同时绑定多个响应函数,当事件被触发时,响应函数将会按照函数的绑定顺序执行。该方法不支持IE8及以下的浏览器 - 参数1:事件的字符串,去掉on,如onclik事件需要传"click"- 参数2:回调函数,当事件触发时调用该函数- 参数3:boolean,是否在捕获阶段触发事件,一般传false。事件捕获阶段含义在事件的传播一章中详细解释,暂且认为就是加载时。- IE8及以下浏览器:使用元素.attachEvent(参数1, 参数2),也可以为一个元素的相同事件绑定多个响应函数,但是执行顺序与addEventListener相反,即先绑定的后执行,后绑定的先执行。 - 参数1:事件的字符串,要on,同addEventListener- 参数2:回调函数,当事件触发时调用该函数- this补充:我们知道函数都有一个隐含参数this,函数的不同使用方式this的含义不同,在事件监听函数(事件处理程序)中 - IE8及以下浏览器,this是window- 其他浏览器的this就是设置了该事件处理程序的元素(从另一个角度想,元素.事件监听函数()本质是以方法的形式调用,故this是调用方法的那个对象,也即前面的元素)<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 首先获取btn01、btn02、btn03var btn01 = document.getElementById("btn01");var btn02 = document.getElementById("btn02");var btn03 = document.getElementById("btn03");/** * 分别为btn01、btn02、btn03绑定事件 * - btn01:适用于IE8及以下浏览器 * - btn02:适用于其他浏览器 * - btn03:适用于兼容场景 */// btn01:attachEvent,先绑定后执行,在IE8及以下浏览器验证请自行取消注释// btn01.attachEvent("onclick", function(){// alert(3);// })// btn01.attachEvent("onclick", function(){// alert(2);// })// btn01.attachEvent("onclick", function(){// alert(1);// alert(this);// })// btn02:addEventListener,先绑定先执行,如果浏览器为IE8及以下浏览器,请将下方代码注释// btn02.addEventListener("click", function(){// alert(1);// alert(this);// }, false)// btn02.addEventListener("click", function(){// alert(2);// }, false)// btn02.addEventListener("click", function(){// alert(3);// }, false)// btn03:兼容模式// 定义一个函数,用来为指定元素绑定响应函数varbind=function(obj, eventStr, callback){if(obj.addEventListener){// 大部分浏览器 obj.addEventListener(eventStr, callback,false);}else{// IE8及以下浏览器 obj.attachEvent("on"+ eventStr, callback);}}// 调用函数bind(btn03,"click",function(){alert(this);})}</script></head><body><buttonid="btn01">IE8及以下浏览器</button><br><br><buttonid="btn02">其他浏览器</button><br><br><buttonid="btn03">兼容模式</button></body></html>

在这里插入图片描述

3.3 事件的冒泡

  • 事件的冒泡(Bubble): - 指事件的向上传导,当后代元素上的事件被触发时,其祖先元素相同事件也会被触发。其本质是向上一层(上一代)传导,如下例中id分别为box1、box2、box3的div元素存在爷爷–爸爸–儿子的关系,此外,body是box1的爸爸,四者都绑定了单击响应函数,当触发box3的onclik事件,向上传导至box2,即触发box2的单击响应函数,box2又向上传导至box1,以此类推。- 以onclik事件为例,由于子元素是父元素(祖先元素)的一部分,故单击子元素,其本质也是单击父元素(祖先元素),从这个角度来看,父元素(祖先元素)的onclik事件必然会触发,当然,我们正好单击了子元素,子元素本身的onclik事件也会触发。- 在开发中,大部分情况冒泡都是有用的,如果不希望发生事件冒泡(单击子元素就是单击子元素本身,不认为单击了父元素),可以通过事件对象来取消冒泡:设置event.cancelBubble=true取消。还是下例当我们设置box3的event.cancelBubble=true时,单击box3只会触发box3的onclik事件,由于取消冒泡,不会传导至box2,box2也因此无法继续传导下去;但是,直接单击box2,依然会触发box1、body的onclik事件。
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text/css">#box1{background-color: yellow;width: 300px;height: 300px;}#box2{background-color: #bfa;width: 200px;height: 200px;}#box3{background-color: gray;width: 100px;height: 100px;}</style><scripttype="text/javascript">
            window.onload=function(){// 获取box1、box2、box3var box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");// 为box3绑定单击响应函数函数
                box3.onclick=function(event){alert("我是box3的单击响应函数");// 取消冒泡:可以将事件对象的cancelBubble设置为true,即可取消冒泡// event = event || window.event;// event.cancelBubble = true;};//为box2绑定一个单击响应函数
                box2.onclick=function(event){alert("我是box2的单击响应函数");// 取消冒泡// event = event || window.event;// event.cancelBubble = true;};// 为box1绑定单击响应函数
                box1.onclick=function(event){alert("我是box1的单击响应函数");// 取消冒泡// event = event || window.event;// event.cancelBubble = true;}//为body绑定一个单击响应函数
                document.body.onclick=function(){alert("我是body的单击响应函数");};};</script></head><body><divid="box1"><divid="box2"><divid="box3"></div></div></div></body></html>

在这里插入图片描述

  • 下述案例为让id为box1的div跟随鼠标移动而移动,但是不会作用于box2(基于上一小节的案例)
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text/css">#box1{width: 100px;height: 100px;background-color: red;/* 开启box1的绝对定位 */position: absolute;}</style><scripttype="text/javascript">
            window.onload=function(){/*
                 * 使div(box1)可以跟随鼠标移动
                 */// 获取box2var box2 = document.getElementById("box2");
                box2.onmousemove=function(event){
                    event = event || window.event;// 取消事件冒泡:当box1移动到box2时,触发box2的onmousemove事件,取消冒泡,// document不会触发onmousemove事件,因此box1不会跟着动
                    event.cancelBubble =true;};// 获取box1var box1 = document.getElementById("box1");// 为document绑定鼠标移动事件
                document.onmousemove=function(event){// 获取滚动条滚动的距离:解决chrome和其他浏览器的兼容问题var st = document.body.scrollTop || document.documentElement.scrollTop;var sl = document.body.scrollLeft || document.documentElement.scrollLeft;// 获取event:解决兼容问题
                    event = event || window.event;// 获取到鼠标在可见窗口的坐标var left = event.clientX;var top = event.clientY;//设置div的偏移量
                    box1.style.left = left + sl +"px";
                    box1.style.top = top + st +"px";};};</script></head><bodystyle="height: 1000px;width: 2000px;"><divid="box2"style="width: 500px;height: 500px;background-color: #bfa;"></div><divid="box1"></div></body></html>

在这里插入图片描述

3.4 事件的委派

  • 在讲解事件的委派前,先来看一个例子(场景):ul祖先元素下有四个li子元素,其中第2-4个li元素均有一个a子元素,分别称作超链接一、超链接二、超链接三,三个a元素的class均为link。我们想要为三个a元素均绑定单击响应函数,可以使用循环遍历每个a并绑定单击响应函数
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script>
        window.onload=function(){var links = document.getElementsByClassName("link");for(var i=0; i < links.length; i++){
                links[i].onclick=function(event){alert("我是超链接的单击响应函数");}}}</script></head><body><!-- <button id="btn01">添加超链接</button> --><ulid="ul"style="background-color: #bfa;"><li><p>p元素</p></li><li><ahref="javascript:;"class="link">超链接一</a></li><li><ahref="javascript:;"class="link">超链接二</a></li><li><ahref="javascript:;"class="link">超链接三</a></li></ul></body></html>

在这里插入图片描述

  • 上述代码通过for循环的方式,虽然也完成了我们的需求:为每个超链接绑定单击响应函数,但是仍存在以下问题: - for循环的方式,进行了3次事件绑定操作,会影响程序的性能- 假如有一个button按钮,点击后会新增一个超链接,我们希望新增的超链接也绑定同样的单击响应事件,上述代码无法完成(只能完成已有超链接的单击响应事件)
  • 解决思路:只绑定一次事件,即可应用到多个元素上,即使元素是后添加的
  • 解决方案:事件的委派: - 指将事件统一绑定给元素(暂且称为目的子元素)共同的祖先元素上,通过祖先元素的event判断是否为目的子元素,如果是,则执行相应的代码。 - event.target:表示触发事件的对象,以onclik为例,假如我们单击的是子元素,则返回子元素对象,假如我们单击的是子元素以外的父元素部分,则返回父元素对象。- 事件的委派本质是为祖先元素绑定事件,但最终效果是为子元素绑定了事件- 事件委派是利用了冒泡,当后代元素事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的相应函数来处理事件。- 通过委派可以减少事件绑定次数,提高了程序性能;还能为新增元素绑定事件,解决了上述问题。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script>
        window.onload=function(){/**
             * 点击button按钮,新增a标签
             */// 获取buttonvar btn = document.getElementById("btn01");// 为button绑定单击响应函数
            btn.onclick=function(){// 创建li标签var liLable = document.createElement("li");// 为创建的li标签添加a标签
                liLable.innerHTML ="<a href='javascript:;' class='link'>新增超链接</a>"// 将创建的li标签添加到ul标签下var ulLable = document.getElementById("ul");
                ulLable.appendChild(liLable);}/**
             * 通过事件的委派,为所有的a标签,包括点击button按钮新增的,绑定单击响应事件
             */// 获取ul标签var ulLable = document.getElementById("ul");// 为ul绑定单击响应事件
            ulLable.onclick=function(event){// 解决浏览器兼容问题
                event = event || window.event;// event.target:获取触发事件的元素对象if(event.target.className =="link"){alert("触发单击响应函数");}}}</script></head><body><buttonid="btn01">添加超链接</button><ulid="ul"style="background-color: #bfa;"><li><p>p元素</p></li><li><ahref="javascript:;"class="link">超链接一</a></li><li><ahref="javascript:;"class="link">超链接二</a></li><li><ahref="javascript:;"class="link">超链接三</a></li></ul></body></html>

在这里插入图片描述

3.5 事件的传播

  • 在网页中标签与标签之间是有嵌套关系的,比如下图,如果这时用户点击了sample按钮,则会以该按钮作为事件目标触发一次点击事件。事件从触发到完成这个过程称为事件传播。
<html><body><div><buttonid="bar">sample</button></div></body></html>
  • 关于事件传播微团公司和网景公司有不同的理解,W3C则综合了两家公司的方案: - 微团公司:事件应该由外向内传播,也就是当事件触发时,应该先触发当前元素事件,然后再向当前元素的祖先元素上传播,也就是说事件应该在冒泡阶段执行- 网景公司:事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素,然后再向内传播给后代元素- W3C:综合了两家公司方案,将事件传播分成三个阶段: - 捕获阶段:这一阶段会从window对象开始向下一直遍历到目标对象,如果发现有对象绑定了响应事件则做相应的处理(从最外层的祖先元素,向目标元素进行事件捕获,但默认此时不会触发事件)- 目标阶段:这一阶段已经遍历结束,事件捕获到目标元素,执行目标对象上绑定的响应函数。- 事件冒泡阶段:这一阶段,事件的传播方式和捕获正好相反,会从事件目标一直向上遍历,直到window对象结束,这时对象上绑定的响应函数也会执行。

在这里插入图片描述

  • 在事件的绑定一节,函数addEventListener()第三个参数为boolean,如果为true则表示在捕获阶段触发事件,一般我们并不希望这样,因此多数情况为false,该参数默认值为false,因此也可以省略不写。实际上,将参数值设为true后,最终的表现结果和false是一样的,由于捕获阶段下一步就是目标阶段,这个过程在后台进行,从前端结果来看,并无区别。
  • IE8及以下浏览器中没有捕获阶段
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text/css">#box1{width: 300px;height: 300px;background-color: yellowgreen;}#box2{width: 200px;height: 200px;background-color: yellow;}#box3{width: 150px;height: 150px;background-color: skyblue;}</style><scripttype="text/javascript">
            window.onload=function(){/*
                 * 分别为三个div绑定单击响应函数
                 */var box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");bind(box1,"click",function(){alert("我是box1的响应函数")});bind(box2,"click",function(){alert("我是box2的响应函数")});bind(box3,"click",function(){alert("我是box3的响应函数")});};functionbind(obj , eventStr , callback){if(obj.addEventListener){//大部分浏览器兼容的方式
                    obj.addEventListener(eventStr , callback ,true);}else{/*
                     * this是谁由调用方式决定
                     * callback.call(obj)
                     *///IE8及以下
                    obj.attachEvent("on"+eventStr ,function(){//在匿名函数中调用回调函数callback.call(obj);});}}</script></head><body><divid="box1"><divid="box2"><divid="box3"></div></div></div></body></html>

在这里插入图片描述

3.6 事件的取消

  • 在讲解事件取消前,我们先来看下例: - 例子:box1-->box2-->box3-->a从左到右辈分逐步降低,其中a是一个超链接,链接到百度首页,同时,a绑定了三个单击响应函数:第一个单击响应函数效果是出现弹窗,内容a1;第二个单击响应函数效果是出现弹窗,内容a2;第三个单击响应函数效果是出现弹窗,内容a3。box1、box2、box3也均绑定了单击响应函数,效果都是出现弹窗,内容分别为"我是box1的单击响应函数"、“我是box2的单击响应函数”、“我是box3的单击响应函数”。- 动作:浏览器打开网页,鼠标单击"百度超链接"- 效果: - 首先连续出现6个弹窗,弹窗内容为:a1、a2、a3、我是box3的单击响应函数、我是box2的单击响应函数、我是box1的单击响应函数- 然后新打开一个网页,跳转至百度首页- 解释:单击"百度超链接",也就是会触发标签a绑定的事件,依次弹出弹窗a1、a2、a3;在事件传播的冒泡阶段,依次触发box3、box2、box1的单击响应函数,故依次出现弹窗"我是box3的单击响应函数"、“我是box2的单击响应函数”、“我是box1的单击响应函数”。超链接a标签本身有默认行为:链接到其他网页并打开,故浏览器打开一个新的网页跳转至百度首页。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script>
        window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/**
             * 为以上四个绑定单击响应函数
             *      - link绑定三个单击响应函数
             *      - box1、box2、box3分别绑定一个单击响应函数
             */// 为link绑定单击响应函数,单击id为link的a元素,依次弹出a1、a2、a3三个窗口// link的第一个单击响应事件if(link.addEventListener){// 一般浏览器
                link.addEventListener("click",function(){alert("a1");})}else{// IE8及以下浏览器
                link.attachEvent("onclick",function(){alert("a3");})}// link的第二个单击响应事件if(link.addEventListener){// 一般浏览器
                link.addEventListener("click",function(){alert("a2");})}else{// IE8及以下浏览器
                link.attachEvent("onclick",function(){alert("a2");})}// link的第三个单击响应事件if(link.addEventListener){// 一般浏览器
                link.addEventListener("click",function(){alert("a3");})}else{// IE8及以下浏览器
                link.attachEvent("onclick",function(){alert("a1");})}// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){
                obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1">
        box1
        <divid="box2">
            box2
            <divid="box3">
                box3
                <br><ahref="http://www.baidu.com"target="_blank"id="link">百度超链接</a></div></div></div></body></html>

在这里插入图片描述

  • 在上例效果的解释中,其实包含了两大方面:一是事件传播;二是默认行为。因此事件取消也是从这两方面就行阐述。

3.6.1 取消事件传播

  • 我们已经知道事件传播包括三个阶段:捕获阶段、目标获取阶段、冒泡阶段。取消事件传播主要针对的是目标阶段和冒泡阶段:- 在目标阶段取消传播:指一个元素本身具有多个相同事件,在其中一个事件设置取消事件传播,该事件以后的事件都不会触发,该事件元素的祖先元素事件也不会触发。如上例标签a具有三个onclik事件,假如我们在第二个onclik事件中设置取消事件传播,则标签a的第三个onclik事件不会触发,box3、box2、box1的onclik事件也不会触发。在标签a的第二个onclik事件中取消事件传播的方法:事件2的event对象.stopImmediatePropagation(),该函数的解释: 不仅阻止事件继续分发到其他document,还会将事件分发就地停止,在当前事件之后注册的其他事件,都不会执行 。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */// 为link绑定单击响应函数,单击id为link的a元素,依次弹出a1、a2、a3三个窗口// link的第一个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a1");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a3");})}// link的第二个单击响应事件:******在响应阶段阻止事件传播*********if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(event){alert("a2"); event.stopImmediatePropagation();})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(event){alert("a2"); event.stopImmediatePropagation();})}// link的第三个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a3");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a1");})}// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){ obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1"> box1 <divid="box2"> box2 <divid="box3"> box3 <br><ahref="http://www.baidu.com"target="_blank"id="link">百度超链接</a></div></div></div></body></html>在这里插入图片描述- 在冒泡阶段阻止事件传播:即目标阶段已经执行结束(目标元素的全部事件都已经触发完成),但阻止下一步的冒泡,也就是说目标事件元素的所有祖先元素(包括父元素)事件都不会触发。- 如上例,在a标签的三个onclick事件中任一个设置event.stopPropagation()都会阻止下一步的冒泡,最终结果为弹窗会弹出a1、a2、a3,但不会出现"我是box3的单击响应函数"、“我是box2的单击响应函数”、"我是box1的单击响应函数"三个弹窗。stopPropagation()函数含义:会阻止事件继续分发到其他document节点,但是当前节点绑定的多个事件会继续按注册的顺序执行 。下方代码,仅在link的第二个单击响应事件处修改了代码:添加event.stopPropagation()``````<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */// 为link绑定单击响应函数,单击id为link的a元素,依次弹出a1、a2、a3三个窗口// link的第一个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a1");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a3");})}// link的第二个单击响应事件:********在冒泡阶段阻止事件传播***********if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(event){alert("a2"); event.stopPropagation();})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(event){alert("a2"); event.stopPropagation();})}// link的第三个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a3");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a1");})}// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){ obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1"> box1 <divid="box2"> box2 <divid="box3"> box3 <br><ahref="http://www.baidu.com"target="_blank"id="link">百度超链接</a></div></div></div></body></html>

在这里插入图片描述

  • 在事件的冒泡一章中,我们提到,可以用event.cancelBubble=true来取消冒泡,和event.stopPropagation()有异曲同工之妙。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */// 为link绑定单击响应函数,单击id为link的a元素,依次弹出a1、a2、a3三个窗口// link的第一个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a1");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a3");})}// link的第二个单击响应事件:********设置event.cancelBubble = true取消冒泡***********if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(event){alert("a2"); event.cancelBubble =true;})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(event){alert("a2"); event.cancelBubble =true;})}// link的第三个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a3");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a1");})}// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){ obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1"> box1 <divid="box2"> box2 <divid="box3"> box3 <br><ahref="http://www.baidu.com"target="_blank"id="link">百度超链接</a></div></div></div></body></html>

在这里插入图片描述

3.6.2 取消默认行为

  • 默认行为:具体指浏览器当前事件的默认行为,比如上例,浏览器对当前超链接a标签的onclik事件具有默认的网页跳转行为(触发完成超链接的onclick事件后,浏览器自动默认执行跳转操作)。类似地,用户点击注册,浏览器自动跳转至注册成功页;用户点击提交按钮后提交成功等等
  • 取消默认行为:对于超链接默认行为加以取消,如取消点击超链接后的跳转,注册信息填写有误时,点击注册不会跳转至注册成功页,提交信息有误时,无法提交成功。
  • 取消默认行为有两种方法:- 方法一:在事件响应函数中使用event.preventDefault(),还是本章开头的案例,在link的第二个onclik事件响应函数中,添加event.preventDefault()代码,最终效果是出现6个弹窗,但网页不会跳转。也就是说浏览器对事件的默认行为是发生在事件传播之后的,无论在link的第几个onclik事件响应函数中添加event.preventDefault(),都不会阻止事件传播,但会取消默认的跳转行为。在IE8及以下浏览器通过event.returnValue=false方式实现。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */// 为link绑定单击响应函数,单击id为link的a元素,依次弹出a1、a2、a3三个窗口// link的第一个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a1");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a3");})}// link的第二个单击响应事件:***event.preventDefault()取消默认行为***if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(event){alert("a2"); event.preventDefault();})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(event){alert("a2"); event.returnValue=false;})}// link的第三个单击响应事件if(link.addEventListener){// 一般浏览器 link.addEventListener("click",function(){alert("a3");})}else{// IE8及以下浏览器 link.attachEvent("onclick",function(){alert("a1");})}// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){ obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1"> box1 <divid="box2"> box2 <divid="box3"> box3 <br><ahref="http://www.baidu.com"target="_blank"id="link">百度超链接</a></div></div></div></body></html>在这里插入图片描述- 方式二:在事件的响应函数中return false,这种方法只适用于通过属性注册的处理程序,也就是说只适用于第一种和第二种事件绑定方式,第三种方法不适用。其他未特别说明,则表示都适用。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><script> window.onload=function(){// 分别获取box1/box2/box3/linkvar box1 = document.getElementById("box1");var box2 = document.getElementById("box2");var box3 = document.getElementById("box3");var link = document.getElementById("link");/** * 为以上四个绑定单击响应函数 * - link、box1、box2、box3分别绑定一个单击响应函数 */// 为link绑定单击响应函数:****使用return false取消默认行为****// 本代码直接在html中绑定了onclik事件,如果想验证此处效果,可删除html代码中的onclick,这里取消注释// link.onclick = function(event){// alert("a");// return false;// }// 为box1、box2、box3分别绑定单击响应函数functionboxFun(obj, content){ obj.onclick=function(){alert(content);}}boxFun(box3,"我是box3的单击响应函数");boxFun(box2,"我是box2的单击响应函数");boxFun(box1,"我是box1的单击响应函数")}</script></head><body><divid="box1"> box1 <divid="box2"> box2 <divid="box3"> box3 <br><!-- 直接在html中绑定onclik事件,如果想验证js效果,可删除此处的onclick,js处取消注释 --><ahref="http://www.baidu.com"target="_blank"id="link"onclick="alert('a');returnfalse;">百度超链接</a></div></div></div></body></html>在这里插入图片描述
标签: javascript 前端 html

本文转载自: https://blog.csdn.net/weixin_43178406/article/details/124655915
版权归原作者 爱编程的喵喵 所有, 如有侵权,请联系我们删除。

“Javascript事件的深度解析与代码实战(监控&amp;冒泡&amp;触发&amp;传播)”的评论:

还没有评论