Finn
Finn

Reputation: 3

Best way to find nearest href attribute?

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.

Upvotes: 0

Views: 2072

Answers (5)

cmbuckley
cmbuckley

Reputation: 42507

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.

Upvotes: 0

Ja͢ck
Ja͢ck

Reputation: 173642

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));
});

Upvotes: 0

Miguel
Miguel

Reputation: 20633

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>

Upvotes: 1

Julio Marchi
Julio Marchi

Reputation: 818

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>

Upvotes: 0

RaymondM
RaymondM

Reputation: 328

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

for example, you can do

var hrefElem = $(target).parent('*[href]');
var link = hrefElem.attr('href');

Upvotes: 0

Related Questions