JavaScript事件模型详解:DOM0与DOM2级事件处理机制

作者:像素猫

时间:2026-10-22

浏览:493

本文深入解析JavaScript中的两种核心事件模型:DOM0级与DOM2级。详细阐述DOM0级事件直接绑定及覆盖特性,并重点讲解DOM2级事件中addEventListener的使用、事件捕获与冒泡的执行顺序,以及如何通过stopPropagation控制事件流。

文章目录

JavaScript事件模型概述

JavaScript主要包含两种事件模型:DOM0级和DOM2级。此前我对这两者的区别理解不够透彻,通过查阅相关资料后,现在对其机制有了清晰的认识。

DOM0级事件模型机制

一、DOM0级事件模型

DOM0级事件模型属于早期标准,所有浏览器均提供支持,且实现逻辑相对简单。具体代码示例如下:


<p id = 'click'>click me</p>
<script>
 document.getElementById('click').onclick = function(event){
  alert(event.target);
 }
</script>

该模型直接在DOM对象上注册事件名称。例如,在p标签上绑定onclick事件,并在回调函数中输出点击的目标元素。解除绑定的方法更为直接,只需将null赋值给该事件处理函数即可,如下所示:

document.getElementById('click'_).onclick = null; 由此可知,在DOM0模型中,一个DOM对象只能注册一个同类型的事件处理函数。若尝试注册多个同类型函数,后注册的函数会覆盖之前的函数,导致先前的绑定失效。


var click = document.getElementById('click');
click.onclick = function(){
 alert('you click the first function');
};
click.onclick = function(){
 alert('you click the second function')
}

在上述代码中,我们为DOM对象绑定了两个onclick函数,但实际执行结果仅触发了第二个注册的函数,前一个函数因被覆盖而未执行。

DOM2级事件模型详解

二、DOM2级事件模型

1. 事件捕获与事件冒泡(capture, bubble)

需要注意的是,IE8及以下版本不支持此事件模型。事件捕获和冒泡的运行机制如下图所示:

JavaScript事件模型概述

如图所示,数字123代表事件捕获阶段,4567代表事件冒泡阶段。首先我们参考以下代码逻辑:


<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
 <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>

假设点击ID为inner的div元素,事件流程如下:首先执行捕获阶段,路径为document→html→body→div(outer);随后执行冒泡阶段,路径为div(inner)→div(outer)→body→html→document。

事件捕获与冒泡原理

2. DOM2级的事件注册与解除

DOM2级事件注册与移除方法

在DOM2模型中,使用addEventListener和removeEventListener方法来注册和移除事件(IE8及更早版本不支持)。相比DOM0,这种方法的显著优势在于一个DOM对象可以注册多个同类型事件,且不会发生覆盖,所有绑定的函数会按顺序依次执行。

语法为addEventListener('事件名称', '事件回调', '捕获/冒泡')。具体示例如下:


<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
 <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
 var click = document.getElementById('inner');
 click.addEventListener('click',function(){
  alert('click one');
 },false);
 click.addEventListener('click',function(){
  alert('click two');
 },false);
</script>

需注意,addEventListener的第一个参数是事件名称,与DOM0级不同,这里不需要加“on”前缀。第三个参数用于指定事件触发阶段:true表示捕获阶段,false表示冒泡阶段。

在上述代码中,我们为inner的div绑定了两个click事件函数,浏览器会依次执行这两个函数。 接下来演示如何利用事件流机制进行控制。


<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
 <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
 var click = document.getElementById('inner');
 var clickouter = document.getElementById('outer');
 click.addEventListener('click',function(){
  alert('inner show');
 },true);
 clickouter.addEventListener('click',function(){
  alert('outer show');
 },true);
</script>

这段代码使用了捕获事件。由于inner嵌套在outer内部,当使用捕获模式时,outer会先于inner捕获到事件。因此执行结果是outer先执行,随后是inner执行。

如果将outer的执行时机改为冒泡阶段呢?


alickouter.addEventListener('click',function(){
 alert('outer show'); 
},false);

此时,执行顺序变为先inner后outer。同理,若将两者的事件触发时机均设为冒泡阶段,结果依然是先执行inner后执行outer。此外,还有一个问题:如果为inner注册两个click事件,一个在捕获阶段,另一个在冒泡阶段(即addEventListener的第三个参数分别设为false和true),执行顺序又是如何?


<script>
 var click = document.getElementById('inner');
 var clickouter = document.getElementById('outer');
 click.addEventListener('click',function(){
  alert('capture show');
 },true);
 click.addEventListener('click',function(){
  alert('bubble show');
 },false);
</script>

在这种情况下,首先执行的是capture show,其次是bubble show。但这一结果与注册顺序相关,先注册的函数先执行。观察事件捕获和冒泡示意图可知,最终具体的DOM对象只有一个。

那么,如果为outer和inner都注册了click事件,但不希望outer执行,该如何处理?此时需要用到stopPropagation函数。该函数用于阻止事件冒泡,即阻止事件继续向父级传播,从而使得后续注册的同类型事件的DOM对象不再执行。

例如在自制下拉框场景中,点击浏览器其他区域时需隐藏下拉框的options,这时就需要用到stopPropagation。代码如下:


<script>
 var click = document.getElementById('inner');
 var clickouter = document.getElementById('outer');
 click.addEventListener('click',function(event){
  alert('inner show');
  event.stopPropagation();
 },false);
 clickouter.addEventListener('click',function(){
  alert('outer show');
 },false);
</script>

正常情况下,不添加stopPropagation时,执行顺序应为先inner后outer。但在inner的事件函数中添加stopPropagation后,执行完inner的逻辑后,事件冒泡在inner处终止,不再继续向上传播,因此outer的事件函数不会被执行。这可以理解为事件冒泡到inner后消失,后续函数不再运行。

由于事件捕获阶段没有提供阻止事件的函数,因此通常将第三个参数设置为事件冒泡(false)。

以上就是全部内容,希望对大家的学习有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章