Как мне обработать щелчок в любом месте страницы, даже когда определенный элемент останавливает распространение? - PullRequest
34 голосов
/ 08 ноября 2011

Мы работаем над инструментом JavaScript, в котором есть старый код, поэтому мы не можем переписать весь инструмент.

Теперь в меню добавлено положение, закрепленное внизу, и клиенту очень хотелось бы, чтобы у него была кнопка переключения для открытия и закрытия меню, за исключением того, что закрытие должно происходить автоматически, когда пользователь начинает делать что-то вне меню, например, когда пользователь возвращается на страницу и выбирает что-то или нажимает на поле формы.

Технически это может работать с событием click на body, которое срабатывает при любом нажатии, однако в старом коде есть множество элементов, в которых событие click было обработано по внутренней ссылке, и return false был добавлен в функцию click, чтобы сайт не мог перейти к тегу href ссылки.

Очевидно, что такая общая функция работает, но не при нажатии на внутреннюю ссылку, где возвращаемое значение false останавливает распространение.

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

Есть ли способ заставить событие щелчка тела все равно, или есть другой способ, которым я могу позволить меню исчезнуть, используя какое-либо глобальное событие щелчка или что-то подобное?

Без необходимости переписывать все остальные клики в приложении, которые были созданы много лет назад. Это было бы чудовищной задачей, тем более что я понятия не имею, как бы я их переписал, без возврата false, но все же не позволяю им перейти к их href.

Ответы [ 7 ]

57 голосов
/ 08 ноября 2011

События в современных реализациях DOM имеют две фазы: захват и всплытие .Фаза захвата - это первая фаза, текущая от defaultView документа к цели события, за которой следует фаза всплытия, которая течет от цели события обратно к defaultView.Для получения дополнительной информации см. http://www.w3.org/TR/DOM-Level-3-Events/#event-flow.

Чтобы обработать фазу захвата события, вам нужно установить третий аргумент для addEventListener в true:

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

К сожалению,как упоминал Уэсли, этап захвата события не может быть обработан надежно или вообще невозможен в старых браузерах.

Одно из возможных решений - вместо этого обработать событие mouseup, поскольку порядок событий для кликов:

  1. mousedown
  2. mouseup
  3. click

Если вы можете быть уверены, что у вас нет обработчиков, отменяющих событие mouseup, тогда этоэто один из способов (и, возможно, лучший способ) идти.Следует также отметить, что многие, если не большинство (если не все), меню пользовательского интерфейса исчезают при нажатии мыши.

6 голосов
/ 08 ноября 2011

В сотрудничестве с Энди 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/

Обратите внимание, приведенный выше код будет работать только с jQuery1.7.Если эти события щелчка были связаны с более ранней версией jQuery или «встроенным», вы все равно можете использовать код, но вам нужно будет по-разному обращаться к «старому обработчику».

Я знаю, я предполагаю,Сценарий «совершенного мира», например, здесь описывается тем, что эти дескрипторы явно вызывают .stopPropagation() вместо возврата false.Так что это все еще может быть бесполезный академический пример, но я чувствую, что могу предложить его: -)

edit : эй, return false; будет работать просто отлично, объекты событий доступнытаким же образом.

3 голосов
/ 08 ноября 2011

Если вы убедитесь, что это обработчик событий first , что-то вроде этого может помочь:

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

Обратите внимание, что если на вашей странице много элементов, это будет действительно очень медленно и не будет работать для чего-либо, добавленного динамически.

2 голосов
/ 07 ноября 2018

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>
2 голосов
/ 08 ноября 2011

Что вы действительно хотите сделать, так это связать обработчик события для фазы захвата события. Однако, насколько я знаю, это не поддерживается в IE, так что это может быть не так уж и полезно.

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

Похожие вопросы:

1 голос
/ 26 апреля 2018

Вы можете использовать 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>
0 голосов
/ 08 ноября 2011

Я думаю, это то, что вам нужно:

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

Это позволит вам вызвать событие щелчка тела из любого места.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...