JavaScript之”冒泡“和“捕捉”详解(全网精品博文)

本文深入解析JavaScript中事件冒泡与捕捉机制,通过实例演示两种机制的工作原理及如何使用。探讨了事件处理的三个阶段:捕捉、目标、冒泡,并讲解了如何阻止事件冒泡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

              JavaScript之“冒泡”和“捕捉”详解(全网精品博文)

 

 

到底什么是“冒泡”?


让我们首先通过一个简单的例子来感性的认识一下,JavaScript的冒泡到底指的是什么东西。

先看下面一个简短的代码片段:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body>
<div onclick="alert('div元素的onClick事件发生了响应')">
  <em>如果你点击<code>EM</code>, 在 <code>DIV元素</code>上绑定的onClick响应函数将会执行</em>
</div>
</body>
</html>

我们在<div>元素上绑定了一个onClick事件发生时的响应函数,让其发出一段警告信息。

这个响应函数虽然是绑定在<div>元素上的,但是当你点击<div>的子元素<em>或者<code>时,<div>元素上的onClick事件一样会被触发,并且执行相应的响应函数。

同学们可以在本地用记事本或者IDE亲自实验一下,或者点击此处进入菜鸟工具提供的HTML/CSS/JS在线开发工具。

使用在线的IDE开发工具,我们可以很方便的在浏览器端直接进行测试。

我们只需要把代码复制后黏贴到在线工具的HTML部分就可以执行了,如下图所示:


当我们点击“em”或者“code元素”时,浏览器将弹出如下所示的警告:

 

那么问题来了,我们明明只在<div>元素上绑定了onClick事件的响应函数,为什么点击<em>元素和<code>元素,<div>元素上绑定的onClick事件一样会被触发呢?

 

“冒泡”的原理


“冒泡”的原理其实非常的简单。

当一个“元素”上面的某个“事件”发生时,这个“元素”上面相对应的“响应函数”将会被调用。然后,这个元素的父元素如果也存在这个“事件”相对应的“响应函数”,父元素上的“响应函数”也会被调用,就这样一直传递到其他的“祖先”元素。类似于古代的“株连九族”,某个人犯错了,他的祖先都要遭到连累,这个事件就像冒泡泡一样,从最内层的元素一直向上层的元素传播,直到传递到不能传递为止。

让我们来看看下面这个例子:

首先进入“”菜鸟工具“”提供的在线HTML编辑工具,分别在“HTML栏”和“CSS栏”输入下面的代码:

HTML栏:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body>
	<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>
</body>
</html>

CSS栏:


  body * {
    margin: 10px;
    border: 1px solid blue;
  }

我们将会看到像下面这样被渲染的网页:

我们首先点击最里面的<p>标签,看看会发生什么?

首先<p>标签上面的“响应函数”被执行了,浏览器弹出“警告框p”,接下来<div>元素上面的“响应函数”也被执行了,又弹出了一次“警告框”,接着<form>元素上面的“响应函数”也被执行了......

再点击<div>元素看看发生了什么?

直接点击<form>元素呢?

做完这个实验,相信你已经发现了些什么,如果没有发现也不用急。

实际上,冒泡就像下面这张图说明的一样:

最“深”层的对象产生的事件不仅会被自身的响应函数所捕获,也会被该对象的所有“祖先”元素所捕获,如果恰好所有“祖先”元素都定义了与该事件相关联的“响应函数”,则这些响应函数都会被执行。正如我们上面看到的,不仅<p>元素上面定义了click事件的“监听器”和“响应函数”,他的所有“祖先”元素:<div>和<form>一样也定义了click事件的监听器”和“响应函数”,因此也会被执行,所以我们点击一次<p>元素,却产生了3次响应。正所谓:“好事不出门,坏事传千里”。

因此,当我们点击<p>元素时,我们将会看到3个警告框:p——>div——form。

向上面这样,子元素产生的事件像冒泡泡一样,会传递到其所有的“祖先”元素的过程,我们就叫做“冒泡”。

 

event.target对象


父元素上的“事件监听器”总是能够获得“事件”发生的“实际详细细节”,比如这个事件是由哪个元素产生的。

产生“事件”的元素叫做“目标元素”,即"target element",我们可以通过event.target获得该元素。

举个栗子来说:如果我们在<form>元素上有一个click事件处理器,这个处理器能够捕捉<form>元素内所有元素发出的click事件,也就是说,不论哪个元素产生了click事件,这个事件都会因为“冒泡”机制而传递到<form>元素上的。<form>元素从而会执行相应的响应函数。

让我们来做下面这个实验:(进入菜鸟HTML在线工具)

HTML栏:

<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="example.css">
</head>

<body>
  A click shows both <code>event.target</code> and <code>this</code> to compare:

  <form id="form">FORM
    <div>DIV
      <p>P</p>
    </div>
  </form>

  <script src="script.js"></script>
</body>
</html>

CSS栏:

form {
  background-color: green;
  position: relative;
  width: 150px;
  height: 150px;
  text-align: center;
  cursor: pointer;
}

div {
  background-color: blue;
  position: absolute;
  top: 25px;
  left: 25px;
  width: 100px;
  height: 100px;
}

p {
  background-color: red;
  position: absolute;
  top: 25px;
  left: 25px;
  width: 50px;
  height: 50px;
  line-height: 50px;
  margin: 0;
}

body {
  line-height: 25px;
  font-size: 16px;
}

JavaScript栏:

form.onclick = function(event) {
  event.target.style.backgroundColor = 'yellow';

  // chrome needs some time to paint yellow
  setTimeout(() => {
    alert("target = " + event.target.tagName + ", this=" + this.tagName);
    event.target.style.backgroundColor = ''
  }, 0);
};

运行后,我们将会看到下面的界面:

我们试着分别点击一下<p>元素,<div>元素和<form>元素,看看event.target和event.currentTarget元素在不同情况下分别指向什么。

我们会发现,当点击<form>元素的时候,此时event.target是等于this的。

 

停止“冒泡”


一个“冒泡事件”会从产生该事件的元素一直向它的父元素传递,直到传到<html>元素,然后到document对象,一些事件甚至可以“冒泡”传递到windows对象上,在事件传递的路径中,其经过的所有元素上相对应的处理器(响应函数)都会被触发。

但是任何处理器(即监听事件和响应函数)都有能力决定某个事件是任由其继续向上层元素“冒泡传递”还是直接停止。

停止事件继续“冒泡传递”的方法为:event.stopPropagation( )。

让我们来举一个例子(将下面的HTML代码直接粘贴到菜鸟工具),在下面这个例子中:如果你点击<button>元素,<body>上的onClick( )事件并不会被触发。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body onclick="alert(`冒泡事件到达了body元素`)">
  <button onclick="event.stopPropagation()">点击我</button>
</body>
</html>

注意:如果不是确实需要,请尽量不要停止“冒泡”!

“冒泡”机制对于开发者来说是很方便的,如果不是确实需要关闭“冒泡”机制,请不要关闭它。

有的时候,event.stopPropagation()会产生隐藏的缺陷以至于最后变成一个很大的问题。

 

“捕捉”


前面我们讲了“冒泡”机制是如何工作的,其实在javascript中还存在另外一种事件处理机制叫做“捕捉”(Capturing)。

标准的DOM 事件描述了3种事件传播机制:

1.捕捉机制:事件从最外部的元素向内部传播。

2.目标机制:事件到达目标元素。

3.冒泡机制:事件从内部向外部传播。

下面这张图描述了当点击<td>元素时,事件在不同机制下的传播路径:

 

可能看图还不太够理解,我们可以亲自来实验一下“冒泡”机制和“捕捉”机制的不同:

我们仍然使用菜鸟工具的HTML在线工具来验证,在相应的区域输入下面的代码:

HTML:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body>
	<form>FORM
  <div>DIV
    <p>P</p>
  </div>
</form>
</body>
</html>

CSS:

body * {
    margin: 10px;
    border: 1px solid blue;
  }

javaScript:

for(let elem of document.querySelectorAll('*')) {
    elem.addEventListener("click", e => alert(`现在是捕捉机制的事件传递过程: ${elem.tagName}`), true);
//设置为true表明使用“捕捉机制”,否则默认使用“冒泡机制”
    elem.addEventListener("click", e => alert(`现在是冒泡机制的事件传递过程: ${elem.tagName}`));
  }

完成上面的输入后,我们将会看到下面的界面:

上面的JS代码给每一个元素都加上了Click事件的响应方法,这样就能很好的看到究竟是哪一个元素上的Click事件被触发了,从而能够直观地看到“捕捉”机制和“冒泡”机制的整个过程。

当我们点击最内部的<p>元素时,此时的触发序列应该是:

1.HTML——>BODY——>FORM——>DIV——>P(捕捉机制生效),接下来是:

2.P——>DIV——>FORM——>BODY——>HTML(冒泡机制生效)

 

如果细心的同学就会发现,<p>元素出现了2次,在“捕捉”机制结束的时候和“冒泡”机制开始的时候。

经过本次实验的你,是不是对“冒泡”机制和“捕捉”机制又有了更深的了解呢?

其实在实际的开发当中,“捕捉”机制非常少用,通常我们使用“冒泡”机制来传递事件。

 

参考文献


【1】https://javascript.info/bubbling-and-capturing 翻译:刘扬俊

 

博客文章版权声明


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值