skip to Main Content

I have a forEach loop that gets the attribute of any element that has the specified attribute on my page. It looks something like this:

const cursorActionElements = document.querySelectorAll("[data-cursor]");

cursorActionElements.forEach((el) => {
 const elAttr = el.getAttribute("data-cursor");

 el.addEventListener('mouseover', () => {
   console.log(elAttr)
 });
});

It all works fine. It goes through and gets all elements that have the data-cursor attribute and stores its value in the elAttr variable. However, for some reason, If the parent of the target happens to ALSO have a data-cursor attribute, it returns both the target’s data-cursor value, AND its parent’s data-cursor value.

I’m really not sure how to solve this issue. I’ve thought of retrieving the data-cursor value on mouseover but, that just means I need to use yet another event listner and it seems to slow my site down a bit.

Please help.

2

Answers


  1. The issue is because the event is propagating up the DOM from the child to the parent, so the event handler fires once per element. To fix this you can call stopPropagation() on the event passed to the event handler.

    Also note that it would be a better idea to read the data attribute from the Event object as well, to avoid any scope/closure issues which may arise when relying on the variable set in a loop within an event handler.

    Here’s a working example with the above changes made:

    const cursorActionElements = document.querySelectorAll("[data-cursor]");
    
    cursorActionElements.forEach((el) => {
      el.addEventListener('mouseover', e => {
        e.stopPropagation();
        const elAttr = e.currentTarget.getAttribute("data-cursor");
        console.log(elAttr)
      });
    });
    <div data-cursor="foo">foo</div>
    <div data-cursor="bar">
      bar
      <div data-cursor="foobar">
        foobar
      </div>
      <div>
        no data attribute
      </div>
    </div>
    Login or Signup to reply.
  2. This is because the mouseover event bubbles up the DOM tree, which means it triggers on the parent element when you hover over a child element. To solve this issue, you can use the mouseenter event instead of mouseover.

    const cursorActionElements = document.querySelectorAll("[data-cursor]");
    
    cursorActionElements.forEach((el) => {
      const elAttr = el.getAttribute("data-cursor");
    
      el.addEventListener('mouseenter', () => {
        console.log(elAttr);
      });
    });

    Now, when you hover over an element with the data-cursor attribute, it will only log the value of that attribute, not the value of any parent elements.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search