tl;博士总结
编写一个函数,当注册以处理层次结构中多个元素上的特定事件时,在冒泡期间到达的第一个元素上执行,但在进一步向上冒泡时不执行(或提前返回)。(实际上没有停止事件的传播。)
简单示例
鉴于此 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>
…单击按钮会产生以下输出:
拖动 BUTTON
点击 DIV
拖动 DIV
但是,我不想拖动 div。具体来说,我只想在它在特定传播链中注册的最叶元素startDrag
上处理一次。<button>
“工作”解决方案
以下 JavaScript 代码已经过测试,可在IE9、Chrome18、FF11、Safari5 和 Opera11 上运行。
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;
}
以上不适用于IE8,即使你使用全局window.event
对象;冒泡期间不会重复使用相同的event
对象。
问题
但是,我不确定上述是否可以保证工作......也不确定它是否尽可能优雅。
- 是否保证在传播过程中将相同的
event
对象向上传递? - 是否保证该
event
对象永远不会被重新用于不同的事件调度? - 对象是否
event
保证支持添加 expando 属性? - 你能想出一种更优雅的方法来检测在当前事件调度中是否已经看到相同的事件处理函数吗?
现实世界的应用
想象一下这个 SVG 层次结构……</p>
<g>
<rect … />
<rect … />
<circle … />
</g>
...以及希望能够通过拖动其中的任何矩形来拖动整个组的用户,并且还希望能够通过拖动来仅拖动圆圈。
现在混入一个为您处理大部分细节的通用拖动库。我提议修改这个库,这样如果用户同时添加一个拖动处理程序,<g>
然后<circle>
在圆圈上拖动会自动阻止拖动在组上启动,但不会停止所有mousedown
事件的传播(因为用户可能有用于其他需要的高级处理程序)。