DOM事件

 


DOM事件流

 

 

   DOM事件流包括三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段。以单击<div>元素这一事件为例,实际的目标元素(<div>)在捕获阶段捕获接收到事件,也即捕获阶段中事件从Document到body就停止了,然后是处于目标阶段,此时事件才在<div>上发生,该阶段在事件处理中被看成是冒泡阶段的一部分(起始点)。最后冒泡阶段发生,事件又传播回Document。

  Opera、FireFox、Chrome和Safari都支持DOM事件流,而IE只支持事件冒泡

  下面是事件捕获和事件冒泡的区别:

复制代码
function myClick(){
    alert(this.nodeName+"点击了按钮");
}
let btn = document.getElementById("mybtn");//获取按钮<input type="button">
//false代表冒泡阶段处理事件,下面两行代码导致点击按钮后,先输出"INPUT点击了按钮" 再输出 "BODY点击了按钮"
btn.addEventListener("click",myClick,false);
document.body.addEventListener("click",myClick,false); //<body>是按钮的上级元素,这里为它也注册事件


//true代表捕获阶段处理事件,下面两行代码导致点击按钮后,先输出"BODY点击了按钮",再输出"INPUT点击了按钮"
btn.addEventListener("click",myClick,true);
document.body.addEventListener("click",myClick,true);
复制代码

添加事件处理程序

DOM 0级事件处理程序

  每个元素都由自己的事件处理程序属性,将该属性的值设置为一个函数,就可以指定事件处理程序。

let btn = document.getElementById("myBtn");
btn.onclick = function{
    alert(this === btn);  
}
btn.onclick = null;删除事件处理程序

 

DOM 2级事件处理程序

  • addEventListener()
  • removeEventListener()

  两个函数都接收三个参数:要处理事件名,事件处理程序函数,布尔值(true表示捕获阶段调用,false表示冒泡阶段调用)。

复制代码
let btn = document.getElementById("mybtn");
btn.addEventListener("click",myClick,false); //注意,事件名是click,不是onclick
//btn.addEventListener("click",myClick2,false); //可以为元素注册多个事件,按顺序进行
btn.removeEventListener("click",myClick,false); //删除事件处理程序

function myClick(){
alert(this.nodeName+"点击了按钮");
}
function myClick2(){
alert("Yes")
}
 
复制代码

  一定要注意:通过addEventListener()添加的事件处理程序只能使用removeEventListener()移除,移除时只需要将相同参数传入removeEventListener()即可。因此使用addEventListener()时,不要使用匿名函数作为事件处理程序,因为当传入同样的匿名函数作为removeEventListener()的参数到时,这个匿名函数与原来传入addEventListener()的匿名函数是两个不同的对象,导致无法移除事件。  

 

IE事件处理程序

  • attachEvent()
  • detachEvent()

  与DOM2一样,不要使用匿名函数作为事件处理程序。由于IE8、Opera6.0及其更早版本不支持DOM 2级事件处理程序,故对于这些版本浏览器可以使用IE事件处理程序。

let btn = document.getElementById("mybtn");
btn.attachEvent("onclick",myClick);  //这里是"onclick"
//btn.attachEvent("onclick",myClick2); //也可以注册多个事件处理程序,但与DOM2级不同,它的执行顺序与添加顺序相反。也即先myClick再myClick
btn.detachEvent("onclick",myClick); 

 

关于事件处理函数的作用域问题

  DOM 0级和DOM 2级的事件处理函数都是在其所属元素的作用域内运行,也即this === btn。而IE的事件处理函数会在全局作用域中进行,也即this === window

 

jQuery中的事件注册

(1)简单注册

$("button").click(function(){   //单击按钮时,按钮变成灰色
    $(this).css("background-color","gray");
});

(2)高级注册

  使用jQuery对象的bind()函数,为匹配到的jQuery对象的每个元素都绑定事件处理程序,对应地,使用unbind()卸载事件处理程序。注意,它与JavaScript中的Function.bind()不是同一个函数,Function.bind()方法的jQuery版本是一个名叫jQuery.proxy()的工具函数。

bind()函数的特性:

  • 除了接收事件类型和处理程序函数之外,这两个参数之间还可以接收任何值作为额外的数据,jQuery会在调用处理程序之前将指定的值设置为Event对象的data属性,而无需使用闭包。
  • 允许为注册的事件处理程序指定命名空间,也就是说可以定义处理程序组。在模块化代码中,当使用多个模块时,我们可能只需要卸载当前模块的事件处理程序而无需卸载其他模块的,使用命名空间可以很好地区分多个模块的相同元素的相同事件,方便后续触发或卸载特定命名空间下的处理程序。

 

使用trigger()函数手动触发事件:

  该方法可以手动触发所有使用jQuery事件注册方法注册的处理程序,也会触发通过submit等HTML属性或Element属性定义的处理程序,但不能手动触发addEventListener()或attachEvent()注册的处理程序。另外,jQuery的事件触发机制是同步的,不涉及事件队列。

复制代码
$("button#btn1").bind("click",myfn1);
$("button#btn2").bind("click",myfn2);
function myfn1(){
    console.log(this);
    $("button#btn2").trigger("click"); //点击按钮1的时候也触发按钮2的单击事件
    console.log("...");
}
function myfn2(){
    console.log(this);
}
复制代码

控制台输出顺序是:

 

 

所谓“同步”,就是在按钮2的单击事件触发时,并没有涉及到事件队列,也即将myfn2加入队列,然后继续执行myfn1中的最后一行,最后再执行myfn2。而是直接处理了myfn2,最后输出“...”。

 

事件对象event

  兼容DOM的浏览器会将一个event对象传入到事件处理函数中。但在IE中event参数是未定义的(undefined),因此可以使用window.event来作为它的事件对象。只有在事件处理程序执行期间,event对象才会存在;一旦处理完毕后,event对象就会被销毁。

 

 

下面是事件的常用属性和方法:

属性/方法

类型

说明

currentTarget

Element

当前正在处理事件的元素,也即与事件处理程序绑定的元素

target

Element

事件的实际目标

type

String

事件的类型

view

AbstractView

与事件关联的视图,等同于发生事件的window对象

preventDefault()

Function

阻止默认事件的执行

stopPropagation()

Function

取消事件的进一步捕获或者冒泡

   

 

复制代码

//为<body>注册事件
document.body.addEventListener("click", function (event){         alert(this.nodeName+"点击了按钮"); //BODY点击了按钮
        alert(event.type);         //click
        alert(event.currentTarget); //[object HTMLBodyElement]
        alert(event.target);     //[object HTMLInputElement] },
false);
复制代码

假如添加了下面代码,事件就不能冒泡到<body>元素处:

let btn = document.getElementById("mybtn");
    btn.addEventListener("click",
        function (event){
            alert(this.nodeName+"点击了按钮"); //INPUT点击了按钮
            event.stopPropagation();  //取消了单击事件的冒泡
            },
        false);

 

事件委托

  如果我像前面那样为每个按钮都添加一个事件处理程序,一旦数量过大就会影响页面的整体运行性能。原因有两点:

  •     每个事件处理函数都是一个对象,都会占用内存;
  •     需要为每个按钮节点指定事件处理程序,DOM访问次数增多

  

  而事件委托,就是利用事件冒泡,将事件处理程序绑定到高层次节点中,用于统一管理某一类型的所有事件。

 

示例如下:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample Page</title>
    <script src="test.js"></script>
</head>
<body>
    <ul id="unordered">
        <li><input type="button" value="Red" id="colorRed" ></li>
        <li><input type="button" value="Green" id="colorGreen"></li>
        <li><input type="button" value="Blue" id="colorBlue"></li>
    </ul>
</body>
</html>
复制代码
复制代码
let element = document.getElementById("unordered"); 
//将事件处理程序绑定到上一级节点<ul>,由于事件冒泡,<input>元素的单击事件会冒泡到<ul>元素上,在这里进行统一处理 element.addEventListener(
"click", function (event){ let target = event.target;
       //根据<input>元素的id来进行不同的处理
switch (target.id){ case "colorRed": alert("Red"); break; case "colorGreen": alert("Green"); break; case "colorBlue": alert("Blue"); break;
        //可以用alert(target.value);语句替代这个条件结构 } },
false);
复制代码

   

  当我们添加新的按钮时,也无需再为新按钮添加事件处理程序。作为<ul>的后代元素,新添加的子元素也会利用事件冒泡将事件冒泡到<ul>上进行处理。

 

实时事件

  Web应用经常动态地创建新元素,为了让新元素自动绑定与老元素相同的事件处理程序,而不用每次都使用bind()函数来给新元素绑定事件处理程序,可以使用jQuery的实时事件注册。

  • delegate()
  • undelegate()

  实时事件依赖事件冒泡,通常我们在$(document)上调用delegate(selector,type,handler),来为document或window上注册一个内部程序函数,当指定类型type的事件冒泡到上下文(如document)时,它会判断事件目标是否匹配选择器字符串selector,若匹配则调用指定的处理程序函数。因此,在冒泡过程中,若中途某个元素的处理程序调用了Event对象的stopPropagation()来取消冒泡,那么该实时事件处理程序就永远不会调用。

复制代码
//$("div").bind("click",myfn);//为button的父元素也注册单击事件
$("div").bind("click",function(){
  console.log(this);
  event.stopPropagation();
});
$(document).delegate("button","click",myfn); setTimeout(function (){ $("#two").after("<button id='btn3'>按钮3</button>"); },2000); //两秒后在<div>后创建一个新按钮 function myfn(){ console.log(this); }
复制代码

  观察控制台可以发现,单击按钮1时,控制台先输出<div>再输出<button>,而不是常规的先输出<button>再冒泡到元素<div>上输出<div>。若在<div>的注册语句更改为标红字段,那么实时事件处理程序就失效了,单击按钮1不会触发事件处理程序,只输出<div>,因为事件冒泡到元素<div>上就停止了。

posted @   ˙鲨鱼辣椒ゝ  阅读(117)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示