Подтвердить что ты не робот

Как обрабатывать клик в любом месте страницы, даже если определенный элемент останавливает распространение?

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

  • MouseDown
  • MouseUp
  • выберите

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

Ответ 2

В сотрудничестве с Энди Е это темная сторона силы:

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. Таким образом, это все еще может быть бесполезным академическим примером, но я чувствовал, что выхожу с ним: -)

edit: hey, return false; будет работать нормально, к объектам событий будет обращаться одинаково.

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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