2

I'm trying to get link (url of clickable elements) with the left mouse click, next method doesn't work for all elements:

function callback(e) {
    if (e.button != 0 ) {
        return;
    }
    alert(e.target.href);
}
document.addEventListener('click', callback, true);

For example for some elements on Youtube site - titles or thumbnails (all of them are clickable and they lead to some video/playlist):

enter image description here

enter image description here

href is undefined

but it's clickable and Google Chrome browser shows a preview of link to which this element leads:

enter image description here

UPDATE

The problem that some A tags on Youtube site wraps other elements inside them: <a href="..."><span>...<span/><div.......></a>

I tried debug mode (inspect), selected some of such element to inspect and it picked <span> inside of <a>.

Additional Solution: https://jsfiddle.net/z2huqjjh/2/ (will be good solution if links (A tags) are dynamically being added to a page)

user924
  • 8,146
  • 7
  • 57
  • 139

1 Answers1

5

By default, events bubble. This means that you could have an element that is nested within 100 others. Clicking on that nested element will cause a click event and that event will propagate up through all the ancestor elements until it is either cancelled or reaches the window.

Now, just about everything in the document is clickable. Just because something is clickable doesn't mean it will navigate to a URL, like an <a> element does.

For example:

document.querySelector("div").addEventListener("click", function(){
  alert("Thanks for clicking me!");
});
<div>I'm a &lt;div&gt; and I don't have an 'href'. But, click me anyway</div>

Since only a few elements actually have an href attribute, you could alter your code to look only at those:

function callback(e) {
  if (e.button != 0 ) {
    return;
  } 
  alert(e.currentTarget.href);
}

// Get all the anchors and place into an array
var anchorArray = Array.from(document.querySelectorAll("a"));

// Loop through the anchors
anchorArray.forEach(function(anchor){
  // Assign a click event handler to each. When the click event
  // bubbles to the element, the callback will be called
  anchor.addEventListener('click', callback);
});
<div>I'm a div - click me anyway</div>
<a href="#"><span>I'm a span inside of an anchor, click me too!</span></a>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • yes I understand that I have to check for a tag (I have it in my code, just didn't include in my question here), seems the problem that some A tags on Youtube site wraps other elements inside them: `>` – user924 Nov 09 '17 at 21:20
  • updated my answer, it chooses `Span `on click event (an example with Youtube element), not `A`, it seems I have to get `Span` parent (`A`) with javascript code – user924 Nov 09 '17 at 21:26
  • @user924 Updated answer to show solution for your scenario. You only need to set up your click handler on the anchors, not the entire document. Then, you don't have to worry about `e.target` having or not having an `href`. – Scott Marcus Nov 09 '17 at 21:38
  • yeah I just chose this method (which will work only for A tags without nested elements) https://stackoverflow.com/questions/8492344/javascript-attach-an-onclick-event-to-all-links#comment79603624_8496919 – user924 Nov 09 '17 at 21:39
  • @user924 Look at my answer. It will work for nested elements. I'm showing that. Not to mention that you should not use `.getElementsByTagName()` unless you want a "live" node list, which isn't right for most use cases. – Scott Marcus Nov 09 '17 at 21:39
  • yes I understand it will work, I just thought my first method would be faster (wanted to avoid querySelectorAll and loops) – user924 Nov 09 '17 at 21:41
  • 1
    @user924 Faster isn't always better. There's nothing at all wrong with `.querySelectorAll()` (it's the workhorse of the DOM API) and loops are the effective way to do a lot with a little code. – Scott Marcus Nov 09 '17 at 21:42
  • 2
    @user924 You are not using the code that I wrote. See **[this](https://jsfiddle.net/rp3j6sf8/1/)**. You are using `e.target`, I am using `e.currentTarget`. – Scott Marcus Nov 09 '17 at 22:25
  • oh.. my fault.. I just didn't look at your `callback` function changes, but I earlier copied it, everything was working then somehow I guess I changed to my old and it stopped working, so I just confused myself. But now everything is definitely ok, Thanks!) – user924 Nov 09 '17 at 22:32
  • omg I also forgot about one function that I used in one of my project, `getLink(e)` (I remember I found it somewhere): https://jsfiddle.net/z2huqjjh/1/ – user924 Nov 09 '17 at 22:48
  • 1
    @user924 Yeah, but my solution is much more performant as it doesn't try or even need to start looking at parent elements. That essentially defeats the purpose and behavior of event bubbling. Let bubbling do the work here. – Scott Marcus Nov 10 '17 at 00:15
  • and it's also worse because it would try to find a link (call `getLink(e)` function) every time we click somewhere even if it's not A element is clicked, so yeah it's better to get only A elements `(querySelectorAll("a")`) and set listener to them – user924 Nov 10 '17 at 07:19
  • I found out that only `getLink(e)` works better for me, `(querySelectorAll("a"))` won't get all links when page is loading (for example on Youtube site) and also `getLink(e)` will be good solution if links (A tags) are dynamically being added to a page – user924 Nov 11 '17 at 19:15
  • Just change my solution by replacing .querySelectorAll() with .getElementsByClassNsme() and remove the dot in front of the class name in the passed string. .getLink() is not a good solution. – Scott Marcus Nov 11 '17 at 20:51
  • Verified that `e.currentTarget.href` is the right one to get the url, even when debugging `e.currentTarget` shows `null` by `console.log`. – Jimmy Chen Sep 22 '22 at 07:19