Javascript 即使某个元素停止传播,我如何处理页面中任意位置的点击?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8054429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I handle a click anywhere in the page, even when a certain element stops the propagation?
提问by Sander
We are working on a JavaScript tool that has older code in it, so we cannot re-write the whole tool.
我们正在开发一个包含旧代码的 JavaScript 工具,因此我们无法重写整个工具。
Now, a menu was added position fixed to the bottom and the client would very much like it to have a toggle button to open and close the menu, except closing needs to happen automatically when a user starts doing things out side of the menu, for example, when a user goes back into the page, and selects something or clicks on a form field.
现在,一个菜单被添加到固定在底部的位置,客户非常希望它有一个切换按钮来打开和关闭菜单,除了当用户开始在菜单之外做事时需要自动关闭,因为例如,当用户返回页面并选择某些内容或单击表单字段时。
This could technically work with a click
event on the body
, triggering on any click,
however there are numerous items in the older code, where a click event was handled on an internal link, and return false
was added to the click function, in order for the site not to continue to the link's href
tag.
这在技术上可以与 上的click
事件一起使用body
,在任何点击时触发,但是在旧代码中有很多项目,其中点击事件在内部链接上处理,return false
并被添加到点击功能中,以便网站不继续链接的href
标签。
So clearly, a general function like this does work, but not when clicked on an internal link where the return false stops the propagation.
很明显,像这样的通用函数确实有效,但当点击内部链接时返回 false 停止传播时无效。
$('body').click(function(){
console.log('clicked');
});
Is there a way I can force the body click event anyway, or is there another way I can let the menu dissappear, using some global click event or anything similar?
有没有办法我可以强制身体点击事件,或者有另一种方法可以让菜单消失,使用一些全局点击事件或类似的东西?
Without having to rewrite all other clicks in the application that were created years ago.
That would be a monster task, especially since I have no clue how I would rewrite them, without the return false, but still don't let them go to their href
.
无需重写多年前创建的应用程序中的所有其他点击。那将是一项艰巨的任务,尤其是因为我不知道如何重写它们,而没有返回 false,但仍然不要让它们转到他们的href
.
回答by Andy E
Events in modern DOM implementations have two phases, capturingand bubbling. The capturing phase is the first phase, flowing from the defaultView
of the document to the event target, followed by the bubbling phase, flowing from the event target back to the defaultView
. For more information, see http://www.w3.org/TR/DOM-Level-3-Events/#event-flow.
现代 DOM 实现中的事件有两个阶段,捕获和冒泡。捕获阶段是第一个阶段,从defaultView
文档的 的 流向事件目标,然后是冒泡阶段,从事件目标流回defaultView
. 有关更多信息,请参阅http://www.w3.org/TR/DOM-Level-3-Events/#event-flow。
To handle the capturing phase of an event, you need to set the third argument for addEventListener
to true
:
要处理事件的捕获阶段,你需要设置第三个参数进行addEventListener
到true
:
document.body.addEventListener('click', fn, true);
Sadly, as Wesley mentioned, the capturing phase of an event cannot be handled reliably, or at all, in older browsers.
遗憾的是,正如 Wesley 所提到的,在旧浏览器中无法可靠地或根本无法处理事件的捕获阶段。
One possible solution is to handle the mouseup
event instead, since event order for clicks is:
一种可能的解决方案是处理mouseup
事件,因为点击的事件顺序是:
- mousedown
- mouseup
- click
- 鼠标按下
- 鼠标向上
- 点击
If you can be sure you have no handlers cancelling the mouseup
event, then this is one way (and, arguably, a better way) to go. Another thing to note is that many, if not most (if not all), UI menus disappear on mouse down.
如果您可以确定没有处理程序取消mouseup
事件,那么这是一种方法(并且可以说是更好的方法)。另一件要注意的事情是,许多(如果不是大多数)(如果不是全部)UI 菜单会在鼠标按下时消失。
回答by jAndy
In cooperation with Andy E, this is the dark side of the force:
与安迪E合作,这就是原力的阴暗面:
var _old = jQuery.Event.prototype.stopPropagation;
jQuery.Event.prototype.stopPropagation = function() {
this.target.nodeName !== 'SPAN' && _old.apply( this, arguments );
};
Example: http://jsfiddle.net/M4teA/2/
示例:http: //jsfiddle.net/M4teA/2/
Remember, if all the events were bound via jQuery, you can handle those cases just here. In this example, we just call the original .stopPropagation()
if we are not dealing with a <span>
.
请记住,如果所有事件都通过 jQuery 绑定,那么您可以在此处处理这些情况。在这个例子中,.stopPropagation()
如果我们不处理<span>
.
You cannot prevent the prevent, no.
你不能阻止阻止,不。
What you could do is, to rewrite those event handlers manually in-code. This is tricky business, but if you know how to access the stored handler methods, you could work around it. I played around with it a little, and this is my result:
您可以做的是,在代码中手动重写这些事件处理程序。这是一件棘手的事情,但如果您知道如何访问存储的处理程序方法,您就可以解决它。我玩了一下,这是我的结果:
$( document.body ).click(function() {
alert('Hi I am bound to the body!');
});
$( '#bar' ).click(function(e) {
alert('I am the span and I do prevent propagation');
e.stopPropagation();
});
$( '#yay' ).click(function() {
$('span').each(function(i, elem) {
var events = jQuery._data(elem).events,
oldHandler = [ ],
$elem = $( elem );
if( 'click' in events ) {
[].forEach.call( events.click, function( click ) {
oldHandler.push( click.handler );
});
$elem.off( 'click' );
}
if( oldHandler.length ) {
oldHandler.forEach(function( handler ) {
$elem.bind( 'click', (function( h ) {
return function() {
h.apply( this, [{stopPropagation: $.noop}] );
};
}( handler )));
});
}
});
this.disabled = 1;
return false;
});
Example: http://jsfiddle.net/M4teA/
示例:http: //jsfiddle.net/M4teA/
Notice, the above code will only work with jQuery 1.7. If those click events were bound with an earlier jQuery version or "inline", you still can use the code but you would need to access the "old handler" differently.
请注意,以上代码仅适用于 jQuery 1.7。如果这些点击事件与早期的 jQuery 版本或“内联”绑定,您仍然可以使用代码,但您需要以不同的方式访问“旧处理程序”。
I know I'm assuming a lot of "perfect world" scenario things here, for instance, that those handles explicitly call .stopPropagation()
instead of returning false
. So it still might be a useless academic example, but I felt to come out with it :-)
我知道我在这里假设了很多“完美世界”场景,例如,这些句柄显式调用.stopPropagation()
而不是返回false
. 所以它仍然可能是一个无用的学术例子,但我觉得它出来了:-)
edit: hey, return false;
will work just fine, the event objects is accessed in the same way.
编辑:嘿,return false;
会工作得很好,事件对象以相同的方式访问。
回答by lonesomeday
If you make sure that this is the firstevent handler work, something like this might do the trick:
如果您确保这是第一个事件处理程序工作,那么像这样的事情可能会奏效:
$('*').click(function(event) {
if (this === event.target) { // only fire this handler on the original element
alert('clicked');
}
});
Note that, if you have lots of elements in your page, this will be Really Very Slow, and it won't work for anything added dynamically.
请注意,如果您的页面中有很多元素,这将非常慢,并且对于动态添加的任何内容都不起作用。
回答by Wesley Petrowski
What you really want to do is bind the event handler for the capture phase of the event. However, that isn't supported in IE as far as I know, so that might not be all that useful.
您真正想做的是为事件的捕获阶段绑定事件处理程序。但是,据我所知,这在 IE 中不受支持,因此可能不是那么有用。
http://www.quirksmode.org/js/events_order.html
http://www.quirksmode.org/js/events_order.html
Related questions:
相关问题:
回答by Ronnie Royston
this
is the key (vs evt.target). See example.
this
是关键(与 evt.target 相比)。参见示例。
document.body.addEventListener("click", function (evt) {
console.dir(this);
//note evt.target can be a nested element, not the body element, resulting in misfires
console.log(evt.target);
alert("body clicked");
});
<h4>This is a heading.</h4>
<p>this is a paragraph.</p>
回答by Ryan
You could use jQuery to add an event listener on the document DOM.
您可以使用 jQuery 在文档 DOM 上添加一个事件侦听器。
$(document).on("click", function () {
console.log('clicked');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
回答by James Johnson
I think this is what you need:
我认为这就是你需要的:
$("body").trigger("click");
This will allow you to trigger the body click event from anywhere.
这将允许您从任何地方触发 body click 事件。