手机版

对原生JavaScript事件的深入分析

时间:2021-10-15 来源:互联网 编辑:宝哥软件园 浏览:

JQuery,一个写得少做得多的框架,用得越多,必然不如原生js。

实际上,蔡晓不想写这个博客,它看起来很初级。然而,当她看到自己无法理解网络上原生js事件的绑定和发布时,她决定科普一下。

首先,小盘我不太懂。我只想和你分享我的想法。

DOM0事件模型

事件模型不断发展,早期的事件模型称为DOM0级别。

DOM0事件模型,所有浏览器都支持。

直接在dom对象上注册事件名称是DOM0编写的,例如:

复制代码如下: document . getelementbyid(' test ')。onclick=function(e){ };

这意味着注册onclick事件。当然,它和这篇文章的意思是一样的:

复制代码如下: document . getelementbyid(' test ')[' onmousemove ']=function(e){ };

没什么,只是两种访问js对象属性的方式。[]的形式主要是解决属性名不是合法标识符的问题。例如,object.123肯定会报告错误,但是object['123']避免了这个问题。同时【】的编写也让js活了起来,用字符串表示属性名,可以在运行时动态绑定事件。

在离家更近的地方,当一个事件被触发时,默认会传入一个参数e,表示事件对象。通过e,我们可以得到很多有用的信息,比如点击的坐标,触发事件的dom元素等等。

同一个dom节点只能注册一个基于dom0的事件,以后注册的同一个事件会覆盖之前注册的事件。例如:

复制代码如下: varb TN=document . getelementbyid(' test ');btn.onmousemove=函数(e){ alert(' ok ');};btn['onmousemove']=函数(e){ alert(' ok1 ');};

结果将输出ok1。

接下来我们来谈谈这个。当一个事件被触发时,这指的是该事件在哪个dom对象上被触发。例如:

复制代码如下: varb TN=document . getelementbyid(' test ');BTN . onmousemove=function(e){ alert(this . id);};

结果是输出测试。因为事件是在id为test的dom节点上注册的,所以当事件被触发时,这当然会表示这个dom节点,这可以理解为事件是由这个dom节点调用的。

因此,取消事件非常简单,只需再次注册事件并将值设置为null,例如:

复制代码如下: varb TN=document . getelementbyid(' test ');btn.onclick=函数(e){ alert(' ok ');};btn.onclick=null

原则是最后注册的事件应该覆盖前一个事件,最后注册的事件设置为null,这将释放事件绑定。

事情还没有结束。DOM0事件模型还涉及直接用html编写的事件。例如:

复制的代码如下: div ID=' test ' class=' test ' onclick=' exec();'/div

这样注册的事件也遵循覆盖原则,只能注册一个事件,最后一个事件生效。

不同的是,以这种方式注册的事件相当于动态调用一个函数(这意味着eval),因此不会传入事件对象。同时,这指的是窗口,它不再是触发事件的dom对象。

DOM2事件模型。

与DOM0相比,DOM2事件模型只理解了以下两点:

Dom2支持用同一个Dom元素注册多个同类事件。

Dom2增加了捕获和冒泡的概念。

DOM2事件由addEventListener和removeEventListener管理,这当然是标准。

但是IE8及以下的浏览器自娱自乐,会产生相应的attachEvent和disconnectevent,由于知识的缺乏,本文就不讨论了。

AddEventListener当然是一个注册的事件。它有三个参数,即‘事件名称’、‘事件回调’和‘捕获/冒泡’。例如:

复制代码如下: varb TN=document . getelementbyid(' test ');btn.addEventListener('click '),函数(e){ alert(' ok ');},false);

不用说,与DOM0相比,事件名称只是去掉了前面。

事件回调也很容易理解。当事件触发时,必须通知您。和dom0一样,回调也会默认传入一个事件参数,这是指触发事件的dom节点。

最后一个参数是布尔值。true代表捕获事件,false代表泡沫事件。其实很容易理解。让我们从示意图开始:

也就是说,当一个元素触发一个事件时,首先要通知的是window,然后是document,依次进入,直到实际触发事件的元素(目标元素),这个过程就是capture。接下来,事件将从目标元素中冒泡,然后依次出来,直到窗口对象。这个过程正在冒泡。

为什么要这样设计?这似乎是由于深厚的历史渊源,配菜也不太懂,我就不废话了。

可以看出,捕获事件是在冒泡事件之前触发的。

假设有这样一个html结构:

复制的代码如下: div id=' test ' class=' test ' div id=' testiner ' class=' test-inner '/div/div。

然后我们在外部div注册两个点击事件,分别是捕获事件和冒泡事件。代码如下:

复制代码如下: varb TN=document . getelementbyid(' test ');//关注BTN事件。addeventlistener ('click '),函数(e){ alert(' ok1 ');},真);//冒泡事件BTN。addeventlistener ('click '),函数(e){ alert(' ok ');},false);

最后点击内层的div,先弹出ok1,再弹出ok。结合上面的示意图,外div相当于图中的体,内div相当于图中的底div,证明了先执行捕获事件,再执行冒泡事件。

为什么强调点击内部div?因为真正触发事件的dom元素必须是内层,所以外层dom元素有机会模拟捕获事件和冒泡事件,这可以从示意图中看到。

如果捕获事件和冒泡事件是在实际触发事件的dom元素上注册的呢?

html结构同上,js代码如下:

复制代码如下: var BTN INNER=document . getelementbyid(' testinner ');//冒泡事件btninner。addeventlistener ('click '),函数(e){ alert(' ok ');},false);//捕捉事件btninner . addeventlistener(' click '),函数(e){ alert(' ok1 ');},真);

当然,点击内部div,结果会先ok,再ok1。理论上应该先触发capture事件,也就是说应该先弹出ok1,但这是相当特殊的,因为我们在真正触发事件的dom元素上注册了事件,相当于在图中的div上注册。从图中可以看出,真正触发事件的dom元素是捕获事件的结束点和冒泡事件的开始点,所以这里我们就不区分事件了,先注册的那个先执行。在本例中,先注册冒泡事件,因此先执行它。

这个原则适用于多个同类事件。例如,如果同时注册三个冒泡事件,执行顺序将与注册顺序一致,先注册后执行。例如:

复制代码如下: var BTN INNER=document . getelementbyid(' testinner ');btninner . addeventlistener(' click '),函数(e){ alert(' ok ');},false);btninner . addeventlistener(' click '),函数(e){ alert(' ok1 ');},false);btninner . addeventlistener(' click '),函数(e){ alert(' ok2 ');},false);

结果当然是ok,ok1和ok2依次弹出。

为了进一步理解事件模型,还有另一个场景。如果外部div和内部div同时注册捕获事件,当单击内部div时,必须首先触发外部div的事件。代码如下:

复制代码如下: varb TN=document . getelementbyid(' test ');var btnInner=document . getelementbyid(' testInner ');btninner . addeventlistener(' click '),函数(e){ alert(' ok ');},真);btn.addEventListener('click '),函数(e){ alert(' ok1 ');},真);

结果是先弹出ok1。

如果外部div和内部div都是注册冒泡事件,那么在点击内部div时,必须先执行内部div事件,原理是一样的。

细心的读者会发现,如果点击内部div,外部div也会触发事件,这似乎是有问题的!

Click显然是内部div,但是外部div的事件也被触发了,确实是个问题。

实际上,当一个事件被触发时,默认情况下会传入一个事件对象。如前所述,这个事件对象上有一个方法:stopPropagation,可以防止冒泡,这样外部div就不能接收事件。代码如下:

复制代码如下: varb TN=document . getelementbyid(' test ');var btnInner=document . getelementbyid(' testInner ');btn.addEventListener('click '),函数(e){ alert(' ok1 ');},false);b内部。addeventlistener ('click '),函数(e){//防止冒泡e . stopperpagation();警报(' ok ');},false);

最后,是时候谈谈如何驳回该事件了。事件取消语法:btn.removeEventListener('事件名称','事件回调','捕获/气泡');

这与绑定事件的参数相同,具体如下:

事件名称,也就是说哪个事件被取消了。

事件回调是一个函数,它必须与注册事件的函数相同。

事件类型,布尔值,它必须与注册事件时的类型一致。

也就是说,名称、回调和类型是不可或缺的,它们共同决定了要消除哪个事件。例如:

复制代码如下: varb TN=document . getelementbyid(' test ');//将回调存储在变量var fn=function(e){ alert('ok ')中;};//绑定BTN。addeventlistener ('click ',fn,false);//消除BTN.removeeventlistener ('click ',fn,false);

如果想取消注册的事件,必须保存回调函数,否则不会取消。

0与DOM0混合。

事情已经一团糟,这是混合使用,不会让人活下去。

别怕,混合使用完全没问题。DOM0模型和DOM2模型遵循各自的规则,互不影响。

总的来说,还是哪个先注册,哪个先执行,剩下的都不算什么。

附言

至此,原生js事件已经谈过了,小盘只知道这些事情。欢迎读者补充其他知识点。

实际上,真正的专家不会愚蠢地登记这么多事件。一般来说,他们只需要在最外层的dom元素中注册一次事件,然后通过捕获和冒泡机制找到真正触发事件的dom元素,最后根据触发事件的dom元素提供的信息调用回调。

也就是说,专家会自己管理事件,而不是依赖浏览器,这样可以提高效率,保证兼容性。JQuery不就是这么做的吗~

好了,教程到此结束。希望对读者有帮助!

版权声明:对原生JavaScript事件的深入分析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。