Javascript 自定义事件处理问题

Javascript Custom Events Handling Problem

我有一个正在查看的简单代码笔,它是一个简单的 table 三行,我正在尝试将事件处理程序附加到每一行 table,并分派按下按钮时的事件。

我没有看到正在处理的附加事件,我认为这是一个上下文问题,但我不是世界上最伟大的Javascript,我做错了什么?

[CodePen]https://codepen.io/dyk3r5/pen/rNjmyqJ

<div>
  <input id="myButton" type=button value="Press Me"/>
</div>
<table id="tfm">
  <thead>
    <tr>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
     <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
     <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

$("#myButton").on("click", function() {
  $("#tfm").find("tbody tr").each(function(index) {
    console.log(index)
    this.addEventListener("customEvent", (event, args) => {
      alert('messageRecieved');
    });
   });
  
  this.dispatchEvent(new CustomEvent("customEvent"), {
    detail: "123"
  })
})

您的代码的问题是您从 table 中的三行中的每一行调度三个事件,它工作得很好,但问题是您正在从中监听这些事件一个完全不同的分支。

让我说得更清楚一点,当一个节点发出一个事件时,该事件只能被它的祖先节点(包括节点本身)捕获(或监听)。

因此,在您的代码中,事件由 table 行 (<tr>) 发出,因此它只能被其任何祖先捕获,例如 <tbody> <table> 等等,但是侦听此事件的节点是 <input id="myButton"> 节点,它位于完全不同的分支上。因此,事件永远不会到达 <input> 节点,您也永远不会看到警报。

我认为这个错误是因为 this 关键字在代码的两个地方都引用了不同的节点。在 each 循环内,this 指的是 table 行 (<tr>),但在循环外(在点击处理程序内)它指的是输入 (<input id="myButton">) 节点。

注意: 代码中还有一个错误,即带有 detail 属性 的对象应该作为第二个参数传递给 CustomEvent 构造函数不作为 dispatchEvent.

的第二个参数

错误代码:

this.dispatchEvent(new CustomEvent("customEvent"), {
  detail: "123",
});

正确代码:

this.dispatchEvent(new CustomEvent("customEvent", {
    detail: "123",
}));

在下面的代码片段中,我将事件侦听器移到了 each 循环中,所以现在事件从 table 行发送,它们也被 table 行。因此,所有三行都发出一个“customEvent”事件,所有三行都在监听一个“customEvent”事件。

因此,第一行调度“customEvent”事件,该事件被第一行的“customEvent”侦听器捕获。 请注意,其他两个侦听器不会捕获此事件,因为它们不在同一分支上,它们实际上是兄弟节点。类似地,其他两行发出“customEvent”事件,该事件是被各自的听众捕获,导致三个警报。

另外,请注意代码当前的结构方式,监听事件的代码应该放在派发事件的代码之前。

$("#myButton").on("click", function() {
  $("#tfm").find("tbody tr").each(function(index) {
    console.log(this)
    this.addEventListener("customEvent", (event, args) => {
      alert('messageRecieved');
    });
    this.dispatchEvent(new CustomEvent("customEvent", {
      detail: "123"
    }));
  });
  console.log(this);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <input id="myButton" type=button value="Press Me" />
</div>
<table id="tfm">
  <thead>
    <tr>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

更新: 基于 OP 对此答案的评论。

如果您希望祖先节点监听从table 行发送的事件,您需要在创建“customEvent”事件时首先设置bubbles: true。这将确保事件冒泡到其祖先节点,然后您可以从任何祖先节点(如正文)收听此事件。

在下面的代码片段中,“customEvent”事件由三行 table 发出,但这次只有正文在侦听“customEvent”事件。但是因为 body 是所有三个节点的祖先,所以它会捕获所有三个事件。

$("#myButton").on("click", function() {
  $("#tfm").find("tbody tr").each(function(index) {
    console.log(this)
    this.dispatchEvent(new CustomEvent("customEvent", {
      bubbles: true,
      detail: "123"
    }))
  });
  console.log(this);
})

document.body.addEventListener("customEvent", (e) => {
  alert("Body Listening To 'customEvent'");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <input id="myButton" type=button value="Press Me" />
</div>
<table id="tfm">
  <thead>
    <tr>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

最后,如果您不想设置 bubbles: true 即您不希望事件冒泡,您可以在事件的捕获阶段监听事件(通过将第三个参数传递给听众)。

$("#myButton").on("click", function() {
  $("#tfm").find("tbody tr").each(function(index) {
    console.log(this)
    this.dispatchEvent(new CustomEvent("customEvent", {
      detail: "123"
    }))
  });
  console.log(this);
})

document.body.addEventListener("customEvent", (e) => {
  alert("Body Listening To 'customEvent' (In Capturing Phase)");
}, true)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <input id="myButton" type=button value="Press Me" />
</div>
<table id="tfm">
  <thead>
    <tr>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

使用document.addEventListener代替this.addEventListener