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

San*_*der 34 javascript jquery events javascript-events

我们正在开发一个包含旧代码的JavaScript工具,因此我们无法重写整个工具.

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

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

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

$('body').click(function(){
  console.log('clicked');
});
Run Code Online (Sandbox Code Playgroud)

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

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

And*_*y E 61

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

要处理事件的捕获阶段,你需要设置第三个参数进行addEventListenertrue:

document.body.addEventListener('click', fn, true); 
Run Code Online (Sandbox Code Playgroud)

遗憾的是,正如Wesley所说,在旧版浏览器中,事件的捕获阶段无法可靠处理,或根本无法处理.

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

  1. 鼠标按下
  2. 鼠标松开
  3. 点击

如果你可以确定没有处理程序取消该mouseup事件,那么这是一种方式(而且,可以说是一种更好的方法).另一件需要注意的事情是,鼠标按下时,许多(如果不是全部)UI菜单都会消失.

  • 对于任何想知道他们是否可以使用这个的人,这里是caniuse页面:http://caniuse.com/#feat=addeventlistener简短回答是肯定的,只要你不关心IE8 (2认同)

Ron*_*ton 8

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");
});
Run Code Online (Sandbox Code Playgroud)
<h4>This is a heading.</h4>
<p>this is a paragraph.</p>
Run Code Online (Sandbox Code Playgroud)


jAn*_*ndy 6

安迪·E合作,这是部队的黑暗面:

var _old = jQuery.Event.prototype.stopPropagation;

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

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

请记住,如果所有事件都是通过jQuery绑定的,那么您可以在这里处理这些情况.在这个例子中,.stopPropagation()如果我们不处理a ,我们只需调用原始文件<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;
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

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