Reputation: 8640
I have a simple codepen I've being looking at, its a simple table with three rows, I'm playing around with attaching event handlers to each row table, and dispatching the event on the press of a button.
I'm not seeing the attached events being handled, I'm thinking its a context issue, but I'm not the worlds greatest with Javascript, what am I doing wrong?
[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"
})
})
Upvotes: 0
Views: 340
Reputation: 8168
The problem with your code is that you're dispatching three events from each of the three rows in the table, which is working perfectly, but the problem is that you're listening for these events from an entirely different branch.
Let me make it more clear, when a node emits an event, that event can only be caught (or listened) by its ancestor nodes (including the node itself).
So, in your code the event is being emitted by the table row (<tr>
), so it can only be caught by any of its ancestors like <tbody>
<table>
and so on, but the node that is listening to this event is the <input id="myButton">
node, which is on an entirely different branch. So, the event never reaches the <input>
node and you never see the alert.
I believe this error is because the this
keyword refers to different nodes in both the places in your code. Inside the each
loop, this
refers to the table rows (<tr>
), but outside the loop (inside the click handler) it refers to the input (<input id="myButton">
) node.
NOTE: There's one more mistake in the code i.e. the object with the detail
property should be passed as the second argument to the CustomEvent
constructor not as the second argument to dispatchEvent
.
Incorrect Code:
this.dispatchEvent(new CustomEvent("customEvent"), {
detail: "123",
});
Correct Code:
this.dispatchEvent(new CustomEvent("customEvent", {
detail: "123",
}));
In code snippet below I've moved the event listener inside the each
loop, so now the events are being dispatched from the table rows and they are also being listened by the table rows. So, all the three rows are emitting a "customEvent" event and all the three rows are listening to a "customEvent" event.
So, the first row dispatches the "customEvent" event, which is caught by the first row's "customEvent" listener. Note that the other two listeners don't catch this event because they are not on the same branch, they are actually sibling nodes. Similarly the other two rows emit the "customEvent" event which is caught by their respective listeners, resulting in three alerts.
Also, note the way how the code is currently structured, the code for listening the event should be placed before the code for dispatching the event.
$("#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>
UPDATE: Based on OP's comment on this answer.
If you wish an ancestor node to listen to the events that are being dispatched from the table row, you need to first set bubbles: true
while creating the "customEvent" event. This will make sure that the event bubbles to its ancestor nodes and then you can listen to this event from any ancestor node (like the body).
In the code snippet below, the "customEvent" events are being emitted by the three table rows but this time only the body is listening to the "customEvent" event. But because the body is the ancestor of all the three nodes it will catch all the three events.
$("#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>
Lastly, if you don't want to set bubbles: true
i.e. you don't want the event to bubble, you can then listen to the event in its capturing phase (by passing a third argument to the listener).
$("#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>
Upvotes: 1
Reputation: 16
use document.addEventListener instead of this.addEventListener
Upvotes: 0