编写一个函数 - 当注册以处理层次结构中多个元素上的特定事件时 - 在冒泡期间到达的第一个元素上执行但在向上层次化时不执行(或提前返回)。 (实际上没有阻止事件的传播。)
鉴于this HTML ...
<!DOCTYPE html>
<body>
<div><button>click</button></div>
<script>
var d = document.querySelector('div'),
b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
b.addEventListener('mousedown',startDrag,false);
function clickPick(){ console.log('click',this.tagName); }
function startDrag(){ console.log('drag',this.tagName); }
</script>
...点击按钮会产生以下输出:
拖动按钮
点击DIV
拖动DIV
但是,我不想拖动div。具体来说,我希望startDrag
只能在<button>
上处理一次,这是在特定传播链中注册的最多叶元素。
IE9,Chrome18,FF11,Safari5和Opera11上的以下JavaScript代码has been tested至工作。
function startDrag(evt){
if (seenHandler(evt,startDrag)) return;
console.log('drag',this.tagName);
}
function seenHandler(evt,f){
if (!evt.handlers) evt.handlers=[];
for (var i=evt.handlers.length;i--;) if (evt.handlers[i]==f) return true;
evt.handlers.push(f);
return false;
}
即使您使用全局window.event
对象,上面的不也可以与IE8一起使用;在冒泡期间不会重复使用相同的event
对象。
但是,我不确定上面是否保证能够正常工作......也不确定它是否尽可能优雅。
event
对象传递给链?event
对象是否保证永远不会被重复用于不同的事件派遣?event
对象是否保证支持添加expando属性?想象一下这个SVG层次结构......
<g>
<rect … />
<rect … />
<circle … />
</g>
...和a user who wants能够通过拖动整个组来拖动整个组,并且还可以通过拖动它来拖动圆圈。
现在混合使用generic dragging library来处理大部分细节。我建议修改此库,以便如果用户向<g>
和<circle>
添加拖动处理程序,则拖动圆圈会自动阻止拖动从群组启动,但不会停止传播所有mousedown
事件(因为用户可能有一个高级处理程序用于其他目的)。
答案 0 :(得分:0)
这是一个通用的解决方案,适用于所有主流浏览器的当前版本......但不适用于IE8,可能保证在未来版本中无法运行:
// Solution supporting arbitrary number of handlers
function seenFunction(evt,f){
var s=evt.__seenFuncs;
if (!s) s=evt.__seenFuncs=[];
for (var i=s.length;i--;) if (s[i]===f) return true;
evt.handlers.push(f);
return false;
}
// Used like so:
function myHandler(evt){
if (seenHandler(evt,myHandler)) return;
// ...otherwise, run code normally
}
或者,这是一个不那么通用的解决方案,虽然稍微但可能没有明显更快(再次,不是在IE8中,也可能不会保证在将来工作):
// Implemented per handler; small chance of error with a conflicting name
function myHandler(evt){
if (evt.__ranMyHandler) return;
// ...otherwise, run code normally
evt.__ranMyHandler = true;
}
我很乐意将接受程序转换为另一个答案,并提供适当的规格。
答案 1 :(得分:-1)
This在Firefox中看起来不错,并且可能适用于非标准浏览器,因为jQuery实际上是使用delegate
和live
来实现的。它使用stopPropagation
和target
,两者都是标准的。
var d = document.querySelector('div'),
b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
function clickPick(evt){
console.log('click', this.tagName);
}
function startDrag(evt){
console.log('drag', evt.target.tagName);
evt.stopPropagation();
}
对我来说,单击按钮时的顺序是相反的。你的“拖动按钮”然后“点击DIV”,而我的则相反。但是,我认为它在原版中是未定义的。
编辑:已更改为使用target
。 9之前的IE使用非标准srcElement
属性,这意味着同样的事情。