30

I don't know if what I'm trying to accomplish is possible at all. I would like to override the default behaviour for all the anchor objects (A tag) for a given HTML page. I know I can loop through all the A elements and dynamically add an onclick call to each one of them from the body element onload method, but I'm looking for a more absolute solution. What I need is that all A elements get assigned an onclick action which calls a method that passes the element href property as an argument, so the following:

<a href="http://domain.tld/page.html">

Dynamically becomes:

<a href="http://domain.tld/page.html" onclick="someMethodName('http://domain.tld/page.html'); return false;">

Like I said, the ideal way to do this would be to somehow override the Anchor class altogether when the document loads. If it's not possible then I'll resort to the loop-through-all A elements method (which I already know how to do).

Taz
  • 3,718
  • 2
  • 37
  • 59
boliva
  • 5,604
  • 6
  • 37
  • 39
  • Well, with event delegation they don't all need to have the event tacked onto them. See "Live" in jQuery, for example. Or read about JavaScript event delegation. – Nosredna Nov 19 '09 at 00:25
  • Typically this should be done to the HTML before it makes its way to the client. If your page is complex you could run into quite a lot of programs with event delegation. – Ben Nov 19 '09 at 00:36
  • Actually, for the "click" event I'd expect less problems with delegation. It's things like tracking the mouse that'll kill ya. – Nosredna Nov 19 '09 at 00:37
  • And see this concerning jQuery's "Live"... http://www.zachleat.com/web/2009/05/08/performance-caveat-with-jquery-selectors-and-live-events/ – Nosredna Nov 19 '09 at 01:08

5 Answers5

43

If you don't want to iterate over all your anchor elements, you can simply use event delegation, for example:

document.onclick = function (e) {
  e = e ||  window.event;
  var element = e.target || e.srcElement;

  if (element.tagName == 'A') {
    someFunction(element.href);
    return false; // prevent default action and stop event propagation
  }
};

Check the above example here.

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • What’d you say is the advantage of not iterating over the anchor elements? In a certain way this is the more obtrusive method, since it means redirecting *all* clicks in the document! – Benji XVI Nov 19 '09 at 00:35
  • 6
    The question explicitly mentions not iterating over the anchor elements. – Ben Nov 19 '09 at 00:37
  • And it's also more efficient when you don't have to go through the elements first. – jtbandes Nov 19 '09 at 00:40
  • @Ben Sure, but gives no valid reason: it’s certainly not in any sense informative to say that setting the onclick property of each anchor tag is un-“absolute”. Just wondering if there was a genuine reason to consider? Apart from performance, which is going to be negligible. – Benji XVI Nov 19 '09 at 00:42
  • 2
    @Benji XVI: One of the advantages of this method is that if more anchor elements are added programmatically (i.e.: content loaded with Ajax) after this event is binded, it will continue to work for those new anchors. – Christian C. Salvadó Nov 19 '09 at 00:42
  • As you said above, Ben, “if your page is complex you could run into quite a lot of problems with event delegation.” In my view this, while clever, is actually a *less* robust method, requires a cross-browser hack, and has no particular advantages. It is quite syntactically elegant, but only for the trivial case. – Benji XVI Nov 19 '09 at 00:44
  • @CMS Good point. If that behaviour is required this could definitely be the superior option. – Benji XVI Nov 19 '09 at 00:47
  • 1
    As usual, the answer on what's best is "it depends." If you have many, many items, all intended to run essentially the same code, delegation is great. If you're going to drop into a switch statement and have a list of different things to do for each id, terrible. – Nosredna Nov 19 '09 at 00:53
  • 2
    CMS: Thanks, your method works perfectly, it's short and sweet, and like you said in one of your comments it fulfills the purpose of applying to all 'A' elements, not only those that were on the page when it was initially rendered - the goal of this was to allow for new 'A' tags from content loaded through AJAX to keep working. Great work! – boliva Nov 20 '09 at 04:19
  • Does returning false from an event listener actually prevent the default behaviour? Where is this documented? – Pasha Skender Apr 17 '16 at 04:42
  • 3
  • This solution worked for me. However, the same with `addEventListener` with `useCapture` set to `true` does not. I wonder if someone has a working solution with `addEventListener`. – Mikaël Mayer Dec 22 '18 at 17:35
8

Using jQuery:

$(function() {
    $("a").click(function() {
        return someMethodName($(this).attr('href'));
    });
});

function someMethodName(href)
{
    console.log(href);
    return false;
}
Chris Fulstow
  • 41,170
  • 10
  • 86
  • 110
1

Similar to Benji XVI's answer, but using querySelectorAll, within for instance a container div with class 'container' And using es6.

const $elems = document.querySelectorAll('.container a')
var elems = Array.from($elems)
elems.map(a => {
  a.onclick = (e) => {
    e.preventDefault()
    window.alert('Clicking external links is disabled')
  }
})
svnm
  • 22,878
  • 21
  • 90
  • 105
0

There is no way to override all anchor elements onlick event. You could add an onclick to the document body and have logic to see if the even propagated up from an anchor element, and perform the event in that case, but this solution could cause problems if your page is complex, and isn't propagating clicks in some cases.

I would stick with the getElementsByTagName solution if its feasible.

Ben
  • 1,531
  • 1
  • 11
  • 9
0

CMS's solution is the best solution. Be sure to note that you can get better performance if you reference the function or check that needs to be run from a string stored within a protype property set on base anchor/element or declaratively on the anchor/element. By using this property the function you want to call can be found and be called from one decision branch as opposed to writing a new decision branch for every possibility. If you break or use a case statement with more than one or two cases you should start considering this, today may be a good day to spot for inversion of control and injection style of coding.

Again, Do not use a case if there are multiple events you need to handle. Use instead string composition to construct the path to the function that needs to be called. This requires a prototype to be added to your "A" elements that define the default and string to be composed. A data-attribute on the element should be able to override the default. Essentially we just want a message and an action to pass by default and be changed declaritively in the markup. The action specified should be published somewhere as an API or other accessable library function and be callable by lib['funName']. Just give it an attribute, no worry of name or id. The prototype and attribute will be usefull for debounce, because you will have to debounce.

There is some guys working on clientside routers that are trying to perfect this.

alignedfibers
  • 195
  • 1
  • 11