How do I handle a click anywhere in the page, even when a certain element stops the propagation?

后端 未结 8 1764
臣服心动
臣服心动 2020-12-04 23:51

We are working on a JavaScript tool that has older code in it, so we cannot re-write the whole tool.

Now, a menu was added position fixed to the bottom and the clien

相关标签:
8条回答
  • 2020-12-05 00:11

    Events in modern DOM implementations have two phases, capturing and 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.

    To handle the capturing phase of an event, you need to set the third argument for addEventListener to 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.

    One possible solution is to handle the mouseup event instead, since event order for clicks is:

    1. mousedown
    2. mouseup
    3. 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.

    0 讨论(0)
  • 2020-12-05 00:12

    If you make sure that this is the first event 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.

    0 讨论(0)
  • 2020-12-05 00:15

    You could use jQuery to add an event listener on the document DOM.

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

    0 讨论(0)
  • 2020-12-05 00:33

    I think this is what you need:

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

    This will allow you to trigger the body click event from anywhere.

    0 讨论(0)
  • 2020-12-05 00:36

    this is the key (vs evt.target). See example.

    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>

    0 讨论(0)
  • 2020-12-05 00:37

    In cooperation with Andy E, this is the dark side of the force:

    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/

    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>.


    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/

    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.

    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 :-)

    edit: hey, return false; will work just fine, the event objects is accessed in the same way.

    0 讨论(0)
提交回复
热议问题