0

Why I don't think it's a duplicate.

Hmm, I think that's slightly different to what I'm asking. I don't want to add a listener to all A tags, rather, substract an href attribute, if any, on a click event's target.

I'm trying to "Ajaxify" my whole site, so that when an user clicks ANY link contained within the page, a script sends the "url" or HREF attribute (of the clicked element) to an Ajax function, which in return, renders the requested content (as indicated by the link clicked).

I need to find the HREF attribute of the clicked element, if the element is a link. The problem here, is that many elements can be contained within an A tag (because of the way I've structured them), and e.target.href doesn't necessarily always return an HREF attribute.

Here is what I have so far:

function ajaxifyLinks(e) {
    var target = e.target;
    e.preventDefault();
    while(!target.href) {
        target = target.parentNode;
    }
    if(target.href) {
        ajaxLoad(target.href);
    }
}
document.body.addEventListener('click', ajaxifyLinks);

And here are examples of different "clickable" links that I have:

<!-- Link -->
<a href="/cats">
   cats
</a>

<!-- Link -->
<a href="/hello">
   <span>
      <span> hi </span>
   </span>
</a>

<!-- Link -->
<a href="/bye">
   <span>
      bye
   </span>
</a>

As you can see, this is why e.target.href won't always return the HREF attribute, because you are actually clicking a "linked" span element, however, the browser does take you to the link. Why does this happen? And is there any way I can benefit from that behavior? (As in, extracting the location where the browser is taking you, even if you aren't clicking over an A tag).

I don't like my solution, because the while loop just keeps looking up the DOM tree, sometimes needlessly (when e.target isn't a link or contained by a link).

Thanks.

Finn
  • 3
  • 3

5 Answers5

1

If the element clicked does not have an href, it searches it's parents for an element that has an href. Vanilla JS solution.

function findUpTag(el, attr) {
    while (el.parentNode) {
        el = el.parentNode;
        if (el[attr]) {
            return el;
        }
    }
    return null;
}

document.body.onclick = function (event) {
    event.preventDefault();

    var href = event.target.href;
    
    if (!href) {
        var closest = findUpTag(event.target, 'href');
        if (closest) {
            href = closest.href;
        }
    }

    document.getElementById('output').innerHTML = 'element clicked: ' + event.target.nodeName + '<br>closest href: ' + href;
};
a,
output {
    display: block;
}
<!-- Link -->
<a href="/cats">
   cats
</a>

<!-- Link -->
<a href="/hello">
   <span>
      <span> hi </span>
   </span>
</a>

<!-- Link -->
<a href="/bye">
   <span>
      bye
   </span>
</a>

<output id="output"></output>
Miguel Mota
  • 20,135
  • 5
  • 45
  • 64
  • Great, this works well, thank you for the effort! It's better than my use of the while loop, I think. – Finn Mar 18 '15 at 05:41
  • Well it's still using a while loop, but the difference is it stops as soon as it finds an element with an href. – Miguel Mota Mar 18 '15 at 05:46
0

New answer:

<!-- Link -->
<a href="/cats">
   cats
</a>

<!-- Link -->
<a href="/hello">
   <span style="pointer-events: none;">
      <span style="pointer-events: none;"> hi </span>
   </span>
</a>

<!-- Link -->
<a href="/bye">
   <span style="pointer-events: none;">
      bye
   </span>
</a>
Julio Marchi
  • 730
  • 6
  • 15
  • OP mentioned he doesn't want to use jQuery in the comments. – Miguel Mota Mar 18 '15 at 05:14
  • He must have added more details while I was writing the answer... There was not HTML sample when I start answering anyway... However, there is a simple Cross-Browser CSS solution: add pointer-events:none to the SPAN and it should work... ;) The event will pass directly from the SPAN to the parent A tag! – Julio Marchi Mar 18 '15 at 05:16
  • I rewrote my answer... ;) – Julio Marchi Mar 18 '15 at 05:25
  • This is actually a really cool solution, I didn't know you could do that with CSS. I tried this and it works. The only drawback I think, would be if you have DIV elements inside a link (which I think is now allowed in HTML5), because I assigned that property to div elements, and links became unclickable, for some reason. That's not the case for me, however, and this works as intented. I'm split as to what to pick as an answer, since this, and Moogs' solution work well. – Finn Mar 18 '15 at 05:39
  • It actually varies depending on what you need. I always suggest my team to devote their efforts to create the most simple and concise HTML structure for the products we build. It simplify the process when we need to manipulate DOM objects and events. There is no such thing as "one solution fits all", and the best you can do is to annotate those possibilities and implement them case by case (always keeping in mind that simplicity pays off as the project progresses). – Julio Marchi Mar 18 '15 at 05:48
  • Yeah, it is indeed the simplest answer, and I like your idea of keeping thinks simple. This is a good way to resolve the problem, however, what worries me a bit is that it is a fairly modern property (IE 11 and up), and I think it'd be safer to stick to a pure JS solution for now. (At least in my case). If the support was wider, I'd definitely use this. Thanks a lot. – Finn Mar 18 '15 at 06:11
0

Use the .parent(), ref: http://api.jquery.com/parent/

for example, you can do

var hrefElem = $(target).parent('*[href]');
var link = hrefElem.attr('href');
RaymondM
  • 318
  • 1
  • 10
0

Just attach a click handler to each link on the page:

function ajaxify(e)
{
    e.preventDefault();
    ajaxLoad(this.href);
}

[].forEach.call(document.getElementsByTagName('a'), function(el) {
    el.addEventListener('click', ajaxify.bind(el));
});
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
0

Have a look at What is event bubbling and capturing? to understand how events propagate through the DOM.

The technique of capturing anchor links and loading them via Ajax is referred to as Hijax. Because you're replacing content on the page, you can't simply set up a single event to handle all links, and you probably don't want to keep re-binding every page load, so the approach you are using is good, and is known as event delegation.

The last article describes exactly your problem (right at the very end), and also describes a solution similar to yours, which is really the best way to do it in this scenario. However, you could look at using a microlibrary to simplify some of the event delegation; one example is gator.js.

Community
  • 1
  • 1
cmbuckley
  • 40,217
  • 9
  • 77
  • 91