2015-09-11 7 views
2

Я пытаюсь реализовать простой (-ish) маршрутизатор в качестве упражнения. Тем не менее, у меня возникают проблемы с обнаружением привязки кликов. В идеале я хотел бы перехватить событие в любое время, когда URL-адрес изменится (без использования хэшей), но на данный момент я просто хочу, чтобы можно было перехватить любое событие щелчка якоря.Слушайте все события «onClick» из тегов привязки, которые могут содержать детей

Вот что я пытался до сих пор:

HTML

<nav id="mainMenu"> 
    <ul id="navlinks"> 
     <li> 
      <a href="/"> 
       <span>HOME</span> 
      </a> 
     </li> 
     <li> 
      <a href="/posts"> 
       <span>POSTS</span> 
      </a> 
     </li> 
    </ul> 
</nav> 

Отдельные JS файл

function intercept(event) 
{ 
    console.log("triggered"); 
    var tag = event.target; 

    if (
     tag.tagName.toUpperCase() === 'A' && 
     tag.href && 
     event.button == 0 && 
     tag.origin == document.location.origin 
    ){ 
     event.preventDefault(); 
     console.log("default prevented") 
    } 
} 

С помощью этого кода я только обнаружения span событий, а не <a> событий. Что я могу сделать, чтобы обнаружить события привязки якоря, используя vanilla JS (если возможно)? Я хочу также иметь возможность обнаруживать динамически созданные якоря.

EDIT 1: Изменена функция JS. Консольные печатает default prevented: SPAN:

function intercept(event) 
{ 
    console.log("triggered"); 
    var tag = event.target; 

    //if (
    // tag.tagName === 'A' 
    //){ 
     event.preventDefault(); 
     console.log("default prevented: " + tag.tagName); 
    //} 
} 

EDIT 2: Якорь теги не всегда будет иметь детей, а также возможные дети не могут быть span.

+0

комментария некоторые из ваших условных предикатов, пока он не работает, как ожидалось, то переделки состояния. – dandavis

+0

Чтобы обнаружить попытки навигации, вы можете прослушивать перед загрузкой на теле. – dtanders

+0

@dandavis Все мои условные обозначения исчезли. Он по-прежнему обнаруживает только промежуточные клики. –

ответ

3

Все, что вам нужно сделать, это «подойти к дереву документов» от элемента , пока не найдете тег привязки.

Преимущество этого решения заключается в использовании делегирования событий и может выполняться в любой точке жизненного цикла страницы.Свойство document.documentElement - это тег <html>, и он существует тот момент, когда JavaScript начинает исполняться.

function findParentByTagName(element, tagName) { 
 
    var parent = element; 
 

 
    while (parent !== null && parent.tagName !== tagName.toUpperCase()) { 
 
     parent = parent.parentNode; 
 
    } 
 

 
    return parent; 
 
} 
 

 
function handleAnchorClick(event) { 
 
    event = event || window.event; 
 

 
    if (findParentByTagName(event.target || event.srcElement, "A")) { 
 
     event.preventDefault(); 
 
     console.log("An anchor was clicked!"); 
 
    } 
 
} 
 

 
document.documentElement.addEventListener("click", handleAnchorClick, false);
<p> 
 
    <a href="#"> 
 
    <span> 
 
     <b> 
 
     <i>Click me!</i> 
 
     </b> 
 
    </span> 
 
    </a> 
 
</p> 
 

 
<p> 
 
    <a href="#">Click me too!</a> 
 
</p>

+0

Я немного изменил код, чтобы получить доступ к обнаруженному тегу привязки (присвоить результат «findParentByTargetName» локальной переменной). Я не могу поколебать чувство, что это скорее обходной путь. Возможно, сейчас это невозможно. –

+0

Это основной метод для делегирования событий. Все основные библиотеки JavaScript или фреймворки начинаются с 'event.target' и доходят до дерева документов до тех пор, пока не будет выполнено какое-либо условие. Я создал [Oxydizr] (https://github.com/gburghardt/oxydizr), который является библиотекой делегирования событий, которую вы можете подключить к существующему коду. Это немного более надежное, чем это решение, но я не уверен, что он будет работать для вашего одностраничного приложения. –

1

Его потому, что event.target всегда будет указывать на самый глубокий возможный узел, который был нажат.

event.path - это, по сути, то, что вам нужно, но afaik его не широко внедряется (пока).

Вот как получить массив всех родителей мишени, как event.path уступит:

function getPath(e) { 
    return e.parentElement? [e].concat(getPath(e.parentElement)) : [e]; 
} 

После того, как вы есть, что вы можете проверить, является ли какой-либо из этих элементов является якорем:

var thereIsAnAnchorInMyPath = getPath(event.target) 
    .reduce(function(isAnchor, element) { 
    return isAnchor || /^a$/i.test(element.tagName); 
    }, false); 

Ваш пример, таким образом, следующим образом:

function intercept(event){ 
    console.log("triggered"); 
    var thereIsAnAnchorInMyPath = (event.path || getPath(event.target)) 
     .reduce(function(isAnchor, element) { 
     return isAnchor || /^a$/i.test(element.tagName); 
     }, false); 

    if (thereIsAnAnchorInMyPath){ 
     event.preventDefault(); 
     console.log("default prevented") 
    } 
} 

конечно, было бы гораздо проще просто ttach слушателей событий к якорям:

[].slice.call(querySelectorAll('a')).forEach(function(anchor) { 
    anchor.addEventListener('click', intercept); 
}); 

Но это не будет работать, если (по какой-то причине) новые якоря были вставлены в документ. В этом случае вам нужно будет прикрепить прослушиватель событий к каждому новому якорю, как только он будет вставлен в документ.

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

+0

Это хорошее решение, но я собираюсь пойти с ответом Грега Бургарда, так как его легче понять. –