javascriptattributesevent-listener

Get attribute of target and not parent in forEach loop


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.


Solution

  • 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>