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
我有一个正在查看的简单代码笔,它是一个简单的 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