[英]Stop Event Propagation for a Specific Handler
Write a function that—when registered to handle a specific event on multiple elements in a hierarchy—executes on the first element reached during bubbling but does not execute (or returns early) when bubbling further up the hierarchy.编写一个函数,当注册以处理层次结构中多个元素的特定事件时,在冒泡期间到达的第一个元素上执行,但在进一步向上冒泡时不执行(或提前返回)。 (Without actually stopping propagation of the event.)
(实际上没有停止事件的传播。)
<!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>
…clicking on the button yields this output: …单击按钮会产生以下输出:
drag BUTTON
拖动按钮
click DIV点击 DIV
drag DIV拖动 DIV
However, I don't want to drag the div.但是,我不想拖动 div。 Specifically, I want
startDrag
to only be processed once, on the <button>
, the leaf-most element for which it is registered in a particular propagation chain.具体来说,我希望
startDrag
只在<button>
上被处理一次,这是它在特定传播链中注册的最叶元素。
The following JavaScript code has been tested to work on IE9, Chrome18, Firefox11, Safari5, and Opera11.以下 JavaScript 代码已经过测试,可在IE9、Chrome18、Firefox11、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;
}
The above does not work with IE8, even if you use the global window.event
object;以上不适用于 IE8,即使您使用全局
window.event
对象; the same event
object is not reused during bubbling.在冒泡期间不会重用相同的
event
对象。
However, I'm not sure if the above is guaranteed to work…nor if it's as elegant as it could be.但是,我不确定上述是否可以保证工作......也不确定它是否尽可能优雅。
event
object guaranteed to be passed up the chain during propagation?event
对象是否保证在传播过程中向上传递?event
object guaranteed to never be re-used for different event dispatches? event
对象是否保证永远不会被重新用于不同的事件调度?event
object guaranteed to support having an expando property added to it? event
对象是否保证支持向其添加 expando 属性?Imagine this SVG hierarchy…想象一下这个 SVG 层次结构……
<g>
<rect … />
<rect … />
<circle … />
</g>
…and a user who wants to be able to drag the whole group by dragging on any rect within it, and also to be able to drag just the circle by dragging on it. ...以及希望能够通过拖动其中的任何矩形来拖动整个组的用户,并且还希望能够通过拖动来仅拖动圆圈。
Now mix in a generic dragging library that handles most of the details for you.现在混入一个为您处理大部分细节的通用拖动库。 I'm proposing to modify this library such that if the user adds a drag handler to both the
<g>
and the <circle>
then dragging on the circle automatically prevents the dragging from initiating on the group, but without stopping propagation of all mousedown
events (since the user might have a high level handler for other purposes that is desirable).我提议修改这个库,这样如果用户向
<g>
和<circle>
添加了一个拖动处理程序,那么在圆圈上拖动会自动阻止拖动在组上启动,但不会停止所有mousedown
的传播事件(因为用户可能有一个用于其他目的的高级处理程序)。
Here's a generic solution that works in the current versions of all major browsers...but not in IE8 and may not be guaranteed to work in future versions:这是适用于所有主要浏览器当前版本的通用解决方案......但不适用于 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
}
Alternatively, here is a less-generic solution that is slightly-but-probably-not-noticeably faster (again, not in IE8 and maybe not guaranteed to work in the future):或者,这是一个不太通用的解决方案,它稍微快一点但可能不会明显更快(同样,不在 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;
}
I will happily switch the acceptance to another answer with proper specs provided.我很乐意将接受转换为提供适当规格的另一个答案。
This looks good in Firefox, and can probably be adapted to non-standard browsers, since jQuery essentially does this with delegate
and live
.这在 Firefox 中看起来不错,并且可能适用于非标准浏览器,因为 jQuery 本质上是通过
delegate
和live
做到这一点的。 It uses stopPropagation
and target
, both of which are standard.它使用
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();
}
For me, the order when you click on the button is reversed.对我来说,单击按钮时的顺序是相反的。 Yours has "drag BUTTON" then "click DIV", while mine is the opposite.
你的有“拖动按钮”然后“单击 DIV”,而我的则相反。 However, I think it's undefined in the original.
但是,我认为它在原始文件中是未定义的。
EDIT: Changed to use target
.编辑:更改为使用
target
。 IE before 9 uses a non-standard srcElement
property that means the same thing. 9 之前的 IE 使用非标准的
srcElement
属性,这意味着同样的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.