【问题标题】:Listen to all "onClick" events from anchor tags that could possibly contain children从可能包含子标记的锚标记中侦听所有“onClick”事件
【发布时间】:2015-09-11 18:49:08
【问题描述】:

我正在尝试实现一个简单的(-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 事件,而不是 &lt;a&gt; 事件。我可以做些什么来使用 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

【问题讨论】:

  • 注释掉一些条件谓词,直到它按预期工作,然后重新修改条件。
  • 要检测导航尝试,您可以在 body 上监听 beforeunload。
  • @dandavis 我所有的条件句现在都消失了。它仍然只检测跨度点击。
  • @dtanders 这样可以阻止页面卸载吗?
  • 你是如何绑定处理程序的?

标签: javascript html onclick anchor dom-events


【解决方案1】:

您需要做的就是从event.target 元素“向上遍历文档树”,直到找到一个锚标记。

此解决方案的优势在于它利用事件委托,并且可以在页面生命周期的任何时间点运行。 document.documentElement 属性是 &lt;html&gt; 标记,它在 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>

【讨论】:

  • 我稍微修改了代码,以便可以访问检测到的锚标记(将findParentByTargetName 的结果分配给局部变量)。不过,我无法摆脱这更像是一种解决方法的感觉。也许目前还不可能。
  • 这是进行事件委托的基本方法。所有主要的 JavaScript 库或框架都从 event.target 开始,沿着文档树向上走,直到满足某些条件。我创建了Oxydizr,这是一个可以插入现有代码的事件委托库。它比这个解决方案更强大一点,但我不确定它是否适用于您的单页应用程序。
【解决方案2】:

这是因为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")
    }
}

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

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

但是如果(由于某种原因)新的锚点被插入到文档中,这将不起作用。在这种情况下,您需要将一个事件侦听器附加到每个新锚点,只要它被插入到文档中。

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

【讨论】:

  • 这是一个很好的解决方案,但我会选择 Greg Burghardt 的答案,因为它更容易理解。
猜你喜欢
  • 2021-10-09
  • 2011-11-12
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 2015-09-23
  • 1970-01-01
  • 2014-12-22
相关资源
最近更新 更多