从可能包含子节点的锚标签中收听所有“onClick”事件

时间:2015-09-11 18:49:09

标签: javascript html onclick anchor dom-events

我正在尝试将一个简单的(-ish)路由器作为练习。但是,我在检测锚点击时遇到问题。理想情况下,我想在URL更改时(不使用哈希值)拦截事件,但是现在我只想拦截任何锚点击事件。

这是我到目前为止所尝试的内容:

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(如果可能)检测锚点击事件?我希望能够检测动态创建的锚点。

编辑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);
    //}
}

编辑2 :锚标记并不总是有孩子,可能的孩子可能不是span

2 个答案:

答案 0 :(得分:4)

您需要做的就是#34;走上文档树&#34;从event.target元素开始,直到找到锚标记。

此解决方案的优点是它利用事件委派,并且可以在页面生命周期的任何时刻运行。 document.documentElement属性是<html>标记,它在JavaScript开始执行时就存在。

&#13;
&#13;
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);
&#13;
<p>
  <a href="#">
    <span>
      <b>
        <i>Click me!</i>
      </b>
    </span>
  </a>
</p>

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

答案 1 :(得分:1)

因为event.target将始终指向被点击的最深节点。

event.path本质上是你需要的,但是它没有被广泛实现(尚未)。

以下是如何获取所有目标父项的数组,例如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")
    }
}

当然,将事件监听器附加到锚点会容易得多:

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

但如果(出于某种原因)新锚被插入到文档中,那将无效。在这种情况下,只要将其插入到文档中,就需要将事件侦听器附加到每个新锚点。

或者您可以侦听DOM突变事件,然后查询所有锚点,过滤掉已经附加事件侦听器的那些,将它们添加到列表中(这样您可以在后续事件中对它们进行过滤)并最终附加事件听众。