仅捕获不由锚点点击引起的哈希变化事件

4
我正在尝试使用Javascript来模拟CSS的:target伪类,以便捕获导致页面元素被选中的所有事件。我已经确定了3个触发事件:
  1. window.location.hash在初始化时已经将同一ID的元素定位
  2. 单击指向该元素的锚点
  3. hashchange事件独立于上述事件触发(例如通过window.history API)
由于第2种情况是一个独特的情况,因此它非常重要,因为我想调用click事件的preventDefault。以下是此场景的简化代码:
$('body').on('click', 'a[href*=#]', function filterTarget(clickEvent){
    $(this.hash).trigger('target', [clickEvent]);
});

问题出现在尝试实施方案3时:
$(window).on('hashchange', function filterTarget(hashChangeEvent){
    $(this.hash).trigger('target', [hashChangeEvent]);
});

如果一个target处理程序没有取消方案2的本地行为,当本地行为导致结果hashchange事件时,它将再次被触发。我如何过滤掉这些边缘情况?
解决方案后编辑:roasted的答案提供了关键 - 处理命名空间hashchange事件,然后根据在单击处理程序内处理的逻辑处理器取消绑定和重新绑定。我在这里写了完整的插件

你能举个例子说明一下用户如何使用你所描述的系统吗? - Samuel Reid
@SamuelReid 我正在开发一个富交互式单页Web应用程序,强调渐进增强:所有与JavaScript无关的用户代理概念上可行的交互和导航都使用表单元素和(通常)链接处理 - 大多数情况下哈希用于导航到页面内的元素或显示。但是当应用JavaScript时,我经常希望中断这些事件并替换本机行为。 - Barney
@SamuelReid 举个具体的例子:有一种类似于“脚注查看器”的东西,可以显示详细的引用。原生行为是简单地跳转到相关引用,但增强行为会打开一个模态界面,更好地与引用和源材料参考进行交互。我希望人们能够分享脚注的永久链接,但如果 JS 可用,我仍然希望尽可能使用“脚注查看器”。 - Barney
那么你需要这样做,如果情况2没有取消默认事件,情况3将会运行? - Samuel Reid
@SamuelReid 不是这样的 - 我的意思是说,由于在整体上已经捕获了目标事件,因此来自单击的哈希更改不应触发另一个目标事件。 - Barney
3个回答

5

如果我理解正确,您不希望在单击锚点标签时触发hashchange事件。那么您可以使用命名空间事件设置逻辑:

演示

$('body').on('click', 'a[href*=#]', function (clickEvent) {
    filterTarget(clickEvent,this);  
    $(window).off('hashchange.filter').on('hashchange.tmp', function () {
          $(this).off('hashchange.tmp').on('hashchange.filter', filterTarget);
    });
});
$(window).on('hashchange.filter', filterTarget);

function filterTarget(event,elem) {
    $(elem?elem.hash:window.location.hash).trigger('target', [event]);
    //you could filter depending event.type
    alert(event.type + '::'+ (elem?elem.hash:window.location.hash));
}

这基本上就是我想要的——事件嵌套和链接抛出了我,但现在我有时间来解决它,我相信命名空间过滤是解决方案。我将扩展捕获的 clickEventpreventDefault 来重新启用 hashchange 处理程序,并在我解决问题后发布结果。 - Barney
抱歉没能早些颁发奖励,我一直在等待有时间撰写完成的脚本。但是重点是你的解决方案非常完美,结果对我非常有帮助。谢谢! - Barney

1
如果点击事件已经设置了带有片段的哈希值,那么在哈希变化事件中只需丢弃重复的哈希值即可。
onhashchange=function(e){
  if(e.newURL == e.oldURL ){return; }
 //do your normal hashchange event stuff below:

};

参考: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onhashchange

这可以解决无论是什么触发了变化,都会导致级联问题的困扰。


hashchange 隐式地处理了这个问题,因为在理论上 e.newURL == e.oldURL 的事件不会触发 hashchange(没有变化)。在实际的 hashchange 事件中,如果我想要停止(当 e.newURL != e.oldURL 时),我需要知道根本原因是链接单击还是其他原因 - 这已经触发了 target 事件。 - Barney

0

如果你打算在它上面调用preventDefault,似乎可以使用mousedown而不是click。然后假定hashchange不会被触发。


谢谢您的建议。挑战在于不基于点击事件触发“target”一次,然后再基于“hashchange”触发第二次:实际上,我只希望当“hashchange”不是由“click”(已经触发了“target”事件一次)引起时,“target”才会被触发。 - Barney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接