2

var event = new CustomEvent("custom-event", {bubbles:true, detail:"something"});
var eleA = document.getElementById("A");
var eleB = document.getElementById("B");
eleB.addEventListener("custom-event", function(e) {
    console.log("custom-event caught in element B", e.detail);
});
document.addEventListener("custom-event", function(e) {
    console.log("custom-event caught in document", e.detail);
});
eleA.dispatchEvent(event);
<html>
<body>
<div id="A">A</div>
<div id="B">B</div>
</body>
</html>

I have searched past questions but could not find an answer. I have an element A that triggers a custom event and would like another element B to receive the event and act on it. for example:

var event = new CustomEvent("new-event");
var eleA = document.querySelector("#A");
eleA.dispatchEvent(event);

//...
// somewhere else
...
var eleB = document.querySelector("#B");
eleB.addEventListener("new-event", function(e) {
   console.log('heard');
});

I find that the event is not triggered. I tried adding:

var event = new CustomEvent("new-event", {bubbles:true});

but no difference, but however, if I change the listener to as below it works:

document.addEventListener("new-event", function(e) {
   console.log('heard in document');
});

Is it because of some propagation issue? Isn't there a way I can only listen for it in one or more specific elements?

4
  • Please create a working snippet demonstrating your issue. Commented Jan 31, 2018 at 10:27
  • have added a snipet, gurvinder372. if you remove the document.addEventListener there is no output. See my comment below for the de-coupling use-case Commented Jan 31, 2018 at 11:05
  • In the snippet you have shared, event is being triggered for the parent of A, while B is just the sibling. Commented Jan 31, 2018 at 13:01
  • My use-case being: when A changes, B should know that A has changed. And the logic that cause A to change should not know of the existence of B. Basically, would like to de-couple A and B and create what we normal parlance is a "listener". Not sure if dispatchEvent or something else has to be used. The only observation has been that event can be "heard" by the document in the above approach but not by element B. If that is the way it is designed to work then fine. Thanks Commented Feb 1, 2018 at 11:10

1 Answer 1

3

You're confusing the use of dispatchEvent function.

.dispatchEvent()

Dispatches an Event at the specified EventTarget, invoking the affected EventListeners in the appropriate order. The normal event processing rules (including the capturing and optional bubbling phase) also apply to events dispatched manually with dispatchEvent().


A custom event is dispatched to the listeners of a specific target object. It is not dispatched to all listeners of that event no matter which object the event is dispatched to or which object is being listened to? It basically works exactly like a 'click' event works. The event is dispatched only to a specific object and only the listeners for that event attached to that specific object. Reference

The events will be dispatched using the context of an element and every element listening to that specific event will receive the notification. So, you're not attaching

The notification will be received through the used callbacks in addEventListener() function.

This code snippet shows how elementA, elementB, and document are listening to the same new-event event:

var event = new CustomEvent("new-event");
var eleA = document.querySelector("#A");

eleA.addEventListener("new-event", function(e) {
  console.log('new-event from Element A');
});

eleA.dispatchEvent(event);

var eleB = document.querySelector("#B");
eleB.addEventListener("new-event", function(e) {
  console.log('new-event from Element B');
});

eleB.dispatchEvent(event);

document.addEventListener("new-event", function(e) {
  console.log('heard in document');
});

document.dispatchEvent(event);
<span id='A'></span>
<span id='B'></span>

This code snippet shows how the elements listen to their own Events:

var event = new CustomEvent("new-event");
var eleA = document.querySelector("#A");

eleA.addEventListener("new-event", function(e) {
  console.log('new-event from Element A');
});

var eventB = new CustomEvent("new-eventB");
var eleB = document.querySelector("#B");
eleB.addEventListener("new-eventB", function(e) {
  console.log('new-event from Element B');
});

var eventDoc = new CustomEvent("new-eventDoc");
document.addEventListener("new-eventDoc", function(e) {
  console.log('heard in document');
});

eleA.dispatchEvent(event);
eleB.dispatchEvent(eventB);
document.dispatchEvent(eventDoc);
<span id='A'></span>
<span id='B'></span>

You can create a SharedResource object to notify an event to specific receivers.

This code snippet shows you how:

  • eleA notify to eleB and document.
  • eleC notify to eleB.

//This Object represents a shared result among receivers.
var SharedResource = function(event) {
  this.event = event;
  this.receivers = [];

  this.addReceiver = function(receiver) {
    this.receivers.push(receiver);
  }

  this.addReceivers = function(receivers) {
    this.receivers.push.apply(this.receivers, receivers);
  }

  //This function will loop over receivers array to call the current event.
  this.notify = function() {
    var $self = this;
    this.receivers.forEach(function(receiver) {
      receiver.dispatchEvent(new CustomEvent($self.event));
    });
  }
};

//Element A will send a "broadcast" message to eleB and Document.
var eleA = document.querySelector("#A");
eleA.addEventListener("click", function(e) {
  ABDocSharedResource.notify();
});

var eleB = document.querySelector("#B");
eleB.addEventListener("new-event", function(e) {
  console.log('new-event from Element B');
});

document.addEventListener("new-event", function(e) {
  console.log('new-event from Document');
});

var ABDocSharedResource = new SharedResource('new-event');
ABDocSharedResource.addReceivers([eleB, document]);

//Element C will send a "broadcast" message to eleB.
var CBSharedResource = new SharedResource('new-event');
CBSharedResource.addReceiver(eleB);

var eleC = document.querySelector("#C");
eleC.addEventListener("click", function(e) {
  CBSharedResource.notify();
});
<a href="#" id='A'>Click me! (A) - Broadcast to B and Document</a>
<br>
<a href="#" id='C'>Click me! (C) - Broadcast to B</a>


<span id='B'></span>

Sign up to request clarification or add additional context in comments.

4 Comments

How do I de-couple cause and effect? Suppose A does something and informs the world I have done it. Now if B is interested they would like to do something about it. While C does not care. Hence I thought that A dispatches the event and B should add a listener
Thanks Ele. I liked the approach except that it is tightly coupled. That means if there is a new element D that will need to know about an event that is dispatched by A, we first need to add D to the addReceivers function besides adding the eventListener to D. The reason I speak of decoupling is because each of the entities are in separate JS files being added or removed dynamically.
But will in all probability build such a custom broadcast system
Use global variables, ie. window.myEvents = {}; window.myEvents.theEvent = new ...; then later eL.addEventListener('theEventsName', ...); and finally document.dispatchEvent(window.myEvents.theEvent);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.