如何在页面中的任何位置处理单击,即使某个元素停止传播?

时间:2011-11-08 17:16:45

标签: javascript jquery events javascript-events

我们正在开发一个包含较旧代码的JavaScript工具, 所以我们不能重写整个工具。

现在,添加了一个固定在底部的菜单,客户非常希望有一个切换按钮来打开和关闭菜单, 除了关闭需要在用户开始在菜单旁边执行操作时自动发生,例如,当用户返回页面,并选择某些内容或点击表单字段时。

这在技术上可以与click上的body事件一起使用,触发任何点击, 但旧代码中有许多项目,其中在内部链接上处理了点击事件,并且return false已添加到点击功能中,以便网站无法继续链接的href标签

很明显,像这样的通用函数确实有效,但是当点击内部链接时,返回false会停止传播。

$('body').click(function(){
  console.log('clicked');
});

有没有办法可以强制进行身体点击事件, 或者是否有其他方式可以让菜单消失,使用一些全局点击事件或类似的东西?

无需重写多年前创建的应用程序中的所有其他点击。 这将是一项怪物任务,特别是因为我不知道如何重写它们,而不返回false,但仍然不让它们转到href

8 个答案:

答案 0 :(得分:56)

现代DOM实现中的事件有两个阶段,捕获冒泡。捕获阶段是第一阶段,从文档的defaultView流向事件目标,然后是冒泡阶段,从事件目标流回defaultView。有关详细信息,请参阅http://www.w3.org/TR/DOM-Level-3-Events/#event-flow

要处理事件的捕获阶段,您需要将addEventListener的第三个参数设置为true

document.body.addEventListener('click', fn, true); 

可悲的是,正如Wesley所说,在旧浏览器中无法可靠地处理事件的捕获阶段。

一种可能的解决方案是处理mouseup事件,因为点击的事件顺序是:

  1. 鼠标按下
  2. 鼠标松开
  3. 点击
  4. 如果您确定没有处理程序取消mouseup事件,那么这是一种方式(并且可以说是一种更好的方法)。另外需要注意的是,如果不是大多数(如果不是全部的话),UI菜单会在鼠标按下时消失。

答案 1 :(得分:6)

Andy E 合作,这是力量的黑暗面:

var _old = jQuery.Event.prototype.stopPropagation;

jQuery.Event.prototype.stopPropagation = function() {
    this.target.nodeName !== 'SPAN' && _old.apply( this, arguments );
};     

示例http://jsfiddle.net/M4teA/2/

请记住,如果所有事件都是通过jQuery绑定的,那么您可以在这里处理这些情况。在此示例中,如果我们不处理.stopPropagation(),我们只需调用原始<span>


你无法阻止预防,没有。

您可以做的是,在代码中手动重写这些事件处理程序。这是一个棘手的业务,但如果您知道如何访问存储的处理程序方法,您可以解决它。我玩了一下,这是我的结果:

$( 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;
});

示例http://jsfiddle.net/M4teA/

注意,上面的代码只适用于jQuery 1.7。如果这些点击事件与早期的jQuery版本或“内联”绑定,您仍然可以使用代码,但您需要以不同方式访问“旧处理程序”。

我知道我在这里假设了许多“完美的世界”场景,例如,这些句柄明确地调用.stopPropagation()而不是返回false。所以它仍然可能是一个无用的学术范例,但我觉得它会出来: - )

编辑:嘿,return false;工作得很好,事件对象也以同样的方式访问。

答案 2 :(得分:3)

如果你确定这是第一个事件处理程序工作,那么这样的事情可能会起到作用:

$('*').click(function(event) {
    if (this === event.target) { // only fire this handler on the original element
        alert('clicked');
    }
});

请注意,如果您的页面中有很多元素,那么这将非常慢,并且对于动态添加的任何内容都不起作用。

答案 3 :(得分:2)

您真正想要做的是将事件处理程序绑定到事件的捕获阶段。但是,据我所知,这在IE中是不受支持的,所以这可能不是那么有用。

http://www.quirksmode.org/js/events_order.html

相关问题:

答案 4 :(得分:2)

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>

答案 5 :(得分:1)

您可以使用jQuery在文档DOM上添加事件侦听器。

&#13;
&#13;
    $(document).on("click", function () {
        console.log('clicked');
    });
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
&#13;
&#13;

答案 6 :(得分:0)

我认为这就是你所需要的:

$("body").trigger("click");

这将允许您从任何地方触发身体点击事件。

答案 7 :(得分:0)

document.body.addEventListener("keyup", function(event) {
  if (event.keyCode === 13) {
  event.preventDefault();
  console.log('clicked ;)');
}
});

演示

https://jsfiddle.net/muratkezli/51rnc9ug/6/