Лучший способ найти ближайший атрибут href?

Почему я не думаю, что это дубликат.

Хм, я думаю, что это немного отличается от того, что я спрашиваю. Я не хочу добавлять прослушиватель ко всем тегам A, а скорее вычесть атрибут href, если он есть, для цели события клика.

Я пытаюсь «Ajaxify» весь свой сайт, чтобы, когда пользователь щелкает ЛЮБУЮ ссылку, содержащуюся на странице, скрипт отправлял атрибут «url» или HREF (выбранного элемента) в функцию Ajax, которая, в свою очередь, отображает запрошенный контент (как указано по нажатой ссылке).

Мне нужно найти атрибут HREF выбранного элемента, если элемент является ссылкой. Проблема здесь заключается в том, что многие элементы могут содержаться в теге A (из-за того, как я их структурировал), и e.target.href не обязательно всегда возвращает атрибут HREF.

Вот что у меня есть до сих пор:

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

А вот примеры разных «кликабельных» ссылок, которые у меня есть:

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

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

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

Как видите, именно поэтому e.target.href не всегда возвращает атрибут HREF, потому что вы на самом деле щелкаете по «связанному» элементу span, однако браузер направляет вас по ссылке. Почему это происходит? И есть ли способ извлечь выгоду из такого поведения? (Например, извлечение местоположения, куда ведет вас браузер, даже если вы не нажимаете на тег A).

Мне не нравится мое решение, потому что цикл while просто продолжает искать дерево DOM, иногда без необходимости (когда e.target не является ссылкой или содержится в ссылке).

Спасибо.


person Finn    schedule 18.03.2015    source источник
comment
Извините, но действительно неясно, что вы пытаетесь реализовать. Пожалуйста, опишите фактическую проблему, которую вы пытаетесь решить (какое поведение вы пытаетесь добавить на свой сайт), а не то, что вы пытаетесь решить. Пожалуйста, избегайте проблемы XY, когда вы описываете проблемы с вашим решением, не описывая фактические конечная цель.   -  person jfriend00    schedule 18.03.2015
comment
Вы можете прослушивать события click для большинства элементов HTML.   -  person PM 77-1    schedule 18.03.2015
comment
Хорошо, я отредактировал вопрос. Теперь стало яснее? (Для щедрых комментариев см. отредактированный вопрос выше, спасибо).   -  person Finn    schedule 18.03.2015
comment
Тривиально просто с jQuery ~ $(document).on('click', 'a[href]', function() { var href = $(this).attr('href'); })   -  person Phil    schedule 18.03.2015
comment
Я понимаю это, но я действительно не хочу использовать jQuery в этом проекте. В любом случае, где я могу найти, как это реализует jQuery?   -  person Finn    schedule 18.03.2015
comment
возможный дубликат Как выбрать все теги ‹a› и зарегистрировать событие onclick?   -  person Nishit Maheta    schedule 18.03.2015
comment
Хм, я думаю, что это немного отличается от того, что я спрашиваю. Я не хочу добавлять прослушиватель ко всем тегам A, а скорее вычесть атрибут href, если он есть, для цели события клика.   -  person Finn    schedule 18.03.2015
comment
@Finn в общем теге href используется только в теге .. поэтому лучше добавить прослушиватель событий ко всему телу, лучше ориентироваться только на тег. может быть конфликт кликов тела с другими вашими элементами click   -  person Nishit Maheta    schedule 18.03.2015
comment
@Finn, мое решение работает?   -  person Miguel Mota    schedule 18.03.2015


Ответы (5)


Если щелкнутый элемент не имеет href, он ищет у своих родителей элемент, у которого есть href. Ванильное JS-решение.

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>

person Miguel Mota    schedule 18.03.2015
comment
Отлично, это работает хорошо, спасибо за усилия! Я думаю, это лучше, чем мое использование цикла while. - person Finn; 18.03.2015
comment
Ну, он по-прежнему использует цикл while, но разница в том, что он останавливается, как только находит элемент с href. - person Miguel Mota; 18.03.2015

Используйте .parent(), ссылка: http://api.jquery.com/parent/

например, вы можете сделать

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

person RaymondM    schedule 18.03.2015

Новый ответ:

<!-- 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>

person Julio Marchi    schedule 18.03.2015
comment
ОП упомянул в комментариях, что не хочет использовать jQuery. - person Miguel Mota; 18.03.2015
comment
Должно быть, он добавил больше деталей, пока я писал ответ ... В любом случае, когда я начинал отвечать, не было образца HTML ... Однако есть простое кросс-браузерное решение CSS: добавьте события-указатели: нет в SPAN и это должно работать... ;) Событие будет передаваться непосредственно из SPAN в родительский тег A! - person Julio Marchi; 18.03.2015
comment
Я переписал свой ответ... ;) - person Julio Marchi; 18.03.2015
comment
На самом деле это действительно классное решение, я не знал, что вы можете сделать это с помощью CSS. Я попробовал это, и это работает. Я думаю, что единственным недостатком было бы наличие элементов DIV внутри ссылки (что, как я думаю, теперь разрешено в HTML5), потому что я присвоил это свойство элементам div, и ссылки по какой-то причине стали неактивными. Однако для меня это не так, и это работает так, как задумано. Я разделился, что выбрать в качестве ответа, так как это и решение Moogs работают хорошо. - person Finn; 18.03.2015
comment
На самом деле это зависит от того, что вам нужно. Я всегда предлагаю своей команде направить свои усилия на создание максимально простой и лаконичной HTML-структуры для продуктов, которые мы создаем. Это упрощает процесс, когда нам нужно манипулировать объектами и событиями DOM. Не существует такой вещи, как одно решение, подходящее для всех, и лучшее, что вы можете сделать, — это аннотировать эти возможности и реализовывать их в каждом конкретном случае (всегда помня, что простота окупается по мере продвижения проекта). - person Julio Marchi; 18.03.2015
comment
Да, это действительно самый простой ответ, и мне нравится ваша идея мыслить просто. Это хороший способ решить проблему, однако меня немного беспокоит то, что это довольно современное свойство (IE 11 и выше), и я думаю, что сейчас было бы безопаснее придерживаться чистого решения JS. (по крайней мере в моем случае). Если бы поддержка была шире, я бы определенно использовал это. Большое спасибо. - person Finn; 18.03.2015

Просто прикрепите обработчик кликов к каждой ссылке на странице:

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

[].forEach.call(document.getElementsByTagName('a'), function(el) {
    el.addEventListener('click', ajaxify.bind(el));
});
person Ja͢ck    schedule 18.03.2015

Взгляните на Что такое всплытие и захват событий?, чтобы понять, как события распространяются через ДОМ.

Техника захвата якорных ссылок и их загрузки через Ajax называется Hijax. Поскольку вы заменяете контент на странице, вы не можете просто настроить одно событие для обработки всех ссылок, и вы, вероятно, не хотите повторно связывать каждую загрузку страницы, поэтому подход, который вы используете, хорош, и называется делегированием событий.

В последней статье описана именно ваша проблема (прямо в самом конце), а также описано решение, похожее на ваше, что действительно лучше всего сделать в данном сценарии. Однако вы можете рассмотреть возможность использования микробиблиотеки для упрощения делегирования некоторых событий; например, gator.js.

person cmbuckley    schedule 18.03.2015