1

I am trying to create a custom event and capture that event. Here is my simplified HTML and JavaScript

let parent = document.getElementById('parent');
let child = document.getElementById('child');

parent.addEventListener('custom-event', () => alert("parent"), true);
child.addEventListener('custom-event', () => alert("child"));

let event = new Event('custom-event');
parent.dispatchEvent(event);
<div id="parent">
  <p id="child">
    Hello World
  </p>
</div>

In this case I get the alert "parent" but then I don't get the child popup.

However, If I try using already existing event like "click", it works.

let parent = document.getElementById('parent');
let child = document.getElementById('child');

parent.addEventListener('click', () => alert("parent"), true);
child.addEventListener('click', () => alert("child"));
<div id="parent">
  <p id="child">
    Hello World
  </p>
</div>

Is there any way/work around that I can use to capture custom events?

skywalker212
  • 13
  • 1
  • 7
  • You might need to bubble the event correctly. – Mr. Polywhirl Jun 16 '20 at 11:17
  • 2
    Why would triggering an event in the parent trigger events in the children? – Guy Incognito Jun 16 '20 at 11:18
  • @Mr.Polywhirl can you please explain in a bit detail how it could be connected to bubbling? I'm new to this. – skywalker212 Jun 16 '20 at 11:20
  • @GuyIncognito I'm trying event capturing and I think that does exactly what you said. – skywalker212 Jun 16 '20 at 11:21
  • No it doesn't. – – Guy Incognito Jun 16 '20 at 11:23
  • 2
    As Guy said, why would a non-click even trigger the child? You called the event on the parent. – Mr. Polywhirl Jun 16 '20 at 11:23
  • 1
    @Mr.Polywhirl okay, I thought you can capture any event. What I understood from https://stackoverflow.com/questions/4616694/what-is-event-bubbling-and-capturing was that when event goes down in tree then its capturing and when it goes up in the tree then it's bubbling. Basically what I want is to listen to a custom event for all the childrent of "parent" and when parent dispatches the event then all the children element custom events should be triggered. – skywalker212 Jun 16 '20 at 11:40
  • It does go down the tree, but the children are not part of the tree. The event goes up from where the event originated, but not down from there. Otherwise it would be a complete chaos – every time you clicked on a web page's body, all click listeners on the page would trigger. – Guy Incognito Jun 16 '20 at 11:43

2 Answers2

1

As Moritz stated, you will need to fire the event on the child, as it is the most-inner element. That way the event bubbles up to each parent. The target of the event will always be the child, but currentTarget of the event will be the current element as it goes up the chain.

You must make sure that useCapture is true, for the ancestor elements you want the event to be picked-up for. See: EventTarget.addEventListener.

useCapture

A Boolean indicating that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.

Events that are bubbling upward through the tree will not trigger a listener designated to use capture. Event bubbling and capturing are two ways of propagating events that occur in an element that is nested within another element, when both elements have registered a handle for that event. The event propagation mode determines the order in which elements receive the event. See DOM Level 3 Events and JavaScript Event order for a detailed explanation. If not specified, useCapture defaults to false.

If you remove the useCapture param (the second param) from parent.addEventListener, only the grand-parent will get picked-up, following the child. It will not break the chain, unless you cancel the event in the child.

Note: If you are triggering a non-native event, it is preferred to use the CustomEvent constructor. Again, you could just call the constructor directly if you wanted. This is just a browser-safe wrapper.

let grandParent = document.getElementById('x-grand-parent');
let parent = document.getElementById('x-parent');
let child = document.getElementById('x-child');

grandParent.addEventListener('custom-event', (e) => console.log(e.currentTarget.id), true);
parent.addEventListener('custom-event', (e) => console.log(e.currentTarget.id), true);
child.addEventListener('custom-event', (e) => console.log(e.currentTarget.id));

triggerCustomEvent(child, 'custom-event');

function triggerCustomEvent(el, eventName, options) {
  let opts = Object.assign({
    canBubble: true,
    cancelable: true,
    detail: {}
  }, options);
  let event = null;
  if (window.CustomEvent && typeof window.CustomEvent === 'function') {
    event = new CustomEvent(eventName, {
      detail: opts.detail
    });
  } else {
    event = document.createEvent('CustomEvent');
    event.initCustomEvent(eventName, opts.canBubble, opts.cancelable, opts.detail);
  }
  el.dispatchEvent(event);
}
<div id="x-grand-parent">
  <div id="x-parent">
    <p id="x-child">
      Hello World
    </p>
  </div>
</div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
-2

Try dispatching the event on the child node and you'll get both alerts.

let parent = document.getElementById('parent');
let child = document.getElementById('child');

parent.addEventListener('custom-event', () => alert("parent"), true);
child.addEventListener('custom-event', () => alert("child"));

let event = new Event('custom-event');
child.dispatchEvent(event);
<div id="parent">
  <p id="child">
    Hello World
  </p>
</div>
Moritz Roessler
  • 8,542
  • 26
  • 51