Как обрабатывать клик в любом месте страницы, даже если определенный элемент останавливает распространение?
Мы работаем над инструментом JavaScript, в котором есть более старый код,
поэтому мы не можем переписать весь инструмент.
Теперь в меню было добавлено положение, зафиксированное снизу, и клиенту очень хотелось бы, чтобы у него была кнопка переключения, чтобы открыть и закрыть меню,
за исключением того, что закрытие должно происходить автоматически, когда пользователь начинает делать что-то в стороне от меню, например, когда пользователь возвращается на страницу и выбирает что-то или нажимает на поле формы.
Это может технически работать с событием click
на body
, запускать при каждом нажатии,
однако в старшем коде есть множество элементов, в которых событие клика обрабатывалось по внутренней ссылке, а return false
была добавлена в функцию щелчка, чтобы сайт не продолжал тег link href
.
Так ясно, что такая общая функция работает, но не при нажатии на внутреннюю ссылку, где return false останавливает распространение.
$('body').click(function(){
console.log('clicked');
});
Есть ли способ, которым я могу заставить тело нажать событие в любом случае,
или есть другой способ, которым я могу позволить меню исчезать, используя какое-то глобальное событие щелчка или что-то подобное?
Без необходимости переписывать все остальные клики в приложении, которые были созданы много лет назад.
Это было бы чудовищной задачей, тем более, что я не знаю, как бы я переписал их, без возврата false, но все же не позволяю им идти в их href
.
Ответы
Ответ 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');
}
});
Обратите внимание, что если на вашей странице много элементов, это будет действительно очень медленно, и это не будет работать для чего-либо, добавляемого динамически.