javascripthtmladdeventlistenermouseenterremoveeventlistener

How to stop children from triggering parent mouseenter event


My buttons have one image inside them, and the mouseenter event gets triggered by both the button AND the image. I would like the event to be triggered only once by "hovering" the BUTTON, not the image.

HTML, the only relevant part of the code are the buttons and their IDs.

<div class="catalogue-hidden">
        <div id="product-1" class="product estusFlask">
          <img src="./products/Item_Estus_Flask.png" />
          <span>Estus Flask</span>
          <button id="btn-1" class="button">
            1000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-2" class="product">
          <img src="./products/Mask_of_the_Father.png" />
          <span>Mask of the Father</span>
          <button id="btn-2" class="button">
            8000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-3" class="product">
          <img src="./products/Giant_Armor.png" />
          <span>Giant Armor</span>
          <button id="btn-3" class="button">
            5000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-4" class="product">
          <img src="./products/Giant_Gauntlets.png" />
          <span>Giant Gauntlets</span>
          <button id="btn-4" class="button">
            5000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-5" class="product">
          <img src="./products/Giant_Leggings.png" />
          <span>Giant Leggings</span>
          <button id="btn-5" class="button">
            5000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-6" class="product">
          <img src="./products/Wpn_Zweihander.png" />
          <span>Zweihander</span>
          <button id="btn-6" class="button">
            3500
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-7" class="product">
          <img src="./products/Grass_crest_shield.png" />
          <span>Grass Crest Shield</span>
          <button id="btn-7" class="button">
            1500
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-8" class="product">
          <img src="./products/Havel's_Ring.png" />
          <span>Havel's Ring</span>
          <button id="btn-8" class="button">
            2000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-9" class="product">
          <img src="./products/Ring_of_Favor_and_Protection.png" />
          <span class="fap">Ring of Favor and Protection</span>
          <button id="btn-9" class="button">
            2000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-10" class="product">
          <img src="./products/Pyro_Pyromancy_Flame.png" />
          <span>Pyromancy Flame</span>
          <button id="btn-10" class="button">
            1000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div id="product-11" class="product">
          <img src="./products/Black_Flame.png" />
          <span>Black Flame</span>
          <button id="btn-11" class="button">
            5000
            <img src="./icons/soul_of_a_proud_paladin.png" />
          </button>
        </div>

        <div onclick="stopBgm(); changeVideo(); hideHud()" id="product-12" class="product-hidden">
          <img src="./products/7011.png" />
          <span>Well, what is it?</span>
          <button id="btn-12" class="button-hidden">
            PWN SOME NOOBZ
          </div>

JavaScript


function selectSfx() {
  var audio = document.querySelector(".selectSfx");
  audio.currentTime = 0;
  audio.play();
}

for (let idNumber = 1; idNumber < 13; idNumber++) {
  setTimeout(() => {
    document
      .getElementById(`btn-${idNumber}`)
      .addEventListener("mouseenter", selectSfx, true);

    document
      .getElementById(`btn-${idNumber}`)
      .addEventListener("click", (e) => {
        condition++;
        console.log(condition);
        okSfx();

        document.getElementById(`product-${idNumber}`).className =
          "product-fake";

        document.getElementById(`btn-${idNumber}`).disabled = true;

        document
          .getElementById(`btn-${idNumber}`)
          .removeEventListener("mouseenter", selectSfx, true);

        if (condition >= 11) {
          document.querySelector(".product-hidden").className = "product";
          document.getElementById("btn-12").className = "button";
        }
      });
  }, 4000);
}

When I add the mouseenter event like this, the SelectSfx(); does NOT play when I hover the images within the buttons. The sound plays only once, which is the desired result. But unfortunately I am unable to disable the event once the button gets disabled.

function selectSfx() {
  var audio = document.querySelector(".selectSfx");
  audio.currentTime = 0;
  audio.play();
}

for (let idNumber = 1; idNumber < 14; idNumber++) {
  setTimeout(() => {
    document
      .getElementById(`btn-${idNumber}`)
      .addEventListener("mouseenter", (e) => {
        selectSfx();
      });
  }, 4000);
}

Solution

  • Add the following code just below your "setTimeout" line in your javascript file:

    document
      .getElementById(`btn-${idNumber}`)
      .querySelectorAll('img')
      .forEach((img) => {
        img.style.pointerEvents = 'none';
      });
    

    The code removes pointer events from images that are within the desired buttons.

    Edit:

    Perhaps, the "more correct" answer is to remove the third argument of the 'addEventListener' function call:

    ORIGINAL:

    document
      .getElementById(`btn-${idNumber}`)
      .addEventListener('mouseenter', selectSfx, true);
    
    

    FIXED:

    document
      .getElementById(`btn-${idNumber}`)
      .addEventListener('mouseenter', selectSfx);
    

    When useCapture is set to true, the event listener is triggered during the capturing phase of the event propagation process. This means that the event is first captured by the outermost element and then propagated to the inner elements.

    When useCapture is set to false or not specified, the event listener is triggered during the bubbling phase of the event propagation process. This means that the event is first captured by the innermost element and then propagated to the outer elements.