Search code examples
javascripthtmldom-eventspopover

Popover API: Event bubbling not working on beforetoggle event?


Background

Referencing the MDN documentation on Popover API: https://developer.mozilla.org/en-US/docs/Web/API/Popover_API (not to be confused with Bootstrap's popover).

I have multiple popovers, something like this:

<div id="Popover_1" popover>Content of 1</div>
<div id="Popover_2" popover>Content of 2</div>

I'm attempting to trigger a function if a popover is toggled closed. I fully understand how to add an event listener to each popover and it works just fine. Something like:

window.onload = function() {
    const popovers = document.querySelectorAll("[popover]")
    popovers.forEach(popover => {
        popover.addEventListener("beforetoggle", (event) => {
            if (event.newState === "closed") {
                console.log(event);
            }
        });
    });
}

However, for the sake of simplicity, I'd like to use document.addEventListener instead, as I need to trigger the same function regardless of popover source. My understanding is that it should work due to the event bubbling up. I can't get it to. I recognize that some events do not, but in reviewing the MDN I don't see that it wouldn't.

Question

Therefore, my question is: Am I going about this wrong or is bubble up not working? Or, do I have a basic misunderstanding of what I'm dealing with?


Solution

  • No the event doesn't bubble, I'm not sure what made you think it does, but if you look at the specs, you can see that it's fired in here and here, and that in both cases its bubbles attribute is not set (only the cancelable one is set for the "open" case).

    However, you can still capture that event for delegation:

    document.addEventListener("click", (event) => {
      const btn = event.target.closest("button[data-target]");
      if (!btn) { return; }
      const { target } = btn.dataset;
      document.querySelector(`#Popover_${target}`).togglePopover();
    });
    
    document.addEventListener("beforetoggle", (event) => {
      const { newState, oldState, cancelable } = event;
      console.log({ newState, oldState });
    }, { capture: true });
    <button data-target=1>show 1</button>
    <button data-target=2>show 2</button>
    <div id="Popover_1" popover>Content of 1</div>
    <div id="Popover_2" popover>Content of 2</div>