0

I have a line of HTML code:

<a href="/posts" class="nav__link" data-link><i class="fa fa-file"></i><span>Pages</span></a>

and then I catch the event, cancel the transition by the link using my handler:

document.body.addEventListener("click", e => {
        if (e.target.matches("[data-link]")) {
            e.preventDefault();
            navigateTo(e.target.href);
        }
    });

As long as there is only text in the a tag, everything is fine, but as soon as nested span, i, and so on tags appear in it, this code does not work when you click on them.

How can I change this JS code so that it also applies to nested elements?

SubMGR
  • 3
  • 2
  • Think of `closest` as the _idiomatic_ way to do event delegation. Unfortunately, it still takes a long time for the developer community to adopt event delegation and use the native alternative to jQuery. [What is DOM Event delegation?](/q/1687296/4642212) has an answer using `closest` that is still buried under several others. – Sebastian Simon Feb 19 '22 at 18:31

1 Answers1

2

You might use .closest method:

document.body.addEventListener("click", e => {
  if (e.target.closest('a').matches("[data-link]")) {
    e.preventDefault();
    navigateTo(e.target.href);
  }
});

function navigateTo() {
  console.log("let's go!")
}
a,
a i,
a span {
  display: inline-block;
  padding: .5em;
  border: solid 1px
}
<a href="/posts" class="nav__link" data-link><i class="fa fa-file">Nested element</i> <span>Another Nested element</span> Link tag itself</a>
Kosh
  • 16,966
  • 2
  • 19
  • 34
  • You'll need to replace the `e.target` inside the `if`. Also you can just use `closest`: `e.target.closest('a[data-link]')`. – RoToRa Feb 19 '22 at 18:29
  • Or you can use `e.target.parentElement.matches("[data-link]")` – Gonçalo Bastos Feb 19 '22 at 18:30
  • @GonçaloBastos, `parentElement` won't work for e.g. grandchild elements. – Kosh Feb 19 '22 at 18:34
  • https://ibb.co/Gvxm4Mh I tried, but the problem did not disappear in my case and I have no idea why it happens! The link occupies the area of the square, part of which is occupied by the span tag inside, and so, if I click exactly on the span, then this code will not work. But if I click within the a tag, but not on the span, then the code works. and it's even weirder because here your example works well – SubMGR Feb 19 '22 at 18:39
  • @SubMGR Did you replace the `e.target` inside the `if`? Something like: `const link = e.target.closest('a[data-link]'); if (link) { navigateTo(link.href); }` – RoToRa Feb 19 '22 at 18:41
  • @RoToRa, this is not necessary. The example in my answer works without. – Kosh Feb 19 '22 at 18:46
  • @RoToRa Thanks! :3 This helped solve the problem! But how is it? Why could the condition's code affect? (I call preventDefault before it) – SubMGR Feb 19 '22 at 18:48
  • It's not the condition. That just checks if the link is there. `e.target.href` is accessing attribute of the `span`, which is clicked on and which it doesn't have, instead of the link. – RoToRa Feb 19 '22 at 18:52
  • @RoToRa thanks, now i got that! – SubMGR Feb 19 '22 at 19:08