Как проверить, привязано ли событие клика - JQuery

130

Я привязываю событие щелчка к кнопке:

$('#myButton').bind('click',  onButtonClicked);

В одном сценарии это вызывается несколько раз, поэтому, когда я делаю это, triggerя вижу несколько вызовов ajax, которые я хочу предотвратить.

Как мне это сделать, bindтолько если он не был связан раньше.

пыльный
источник
1
Чувак, я думаю, что у + Конрада Гаруса самый безопасный ответ ( stackoverflow.com/a/6930078/842768 ), подумайте об изменении принятого ответа. Ура! ОБНОВЛЕНИЕ: также проверьте + комментарий Герберта Петерса! Это лучший подход.
giovannipds
Для получения информации о текущих стандартах см. Ответ @A Morel ниже ( stackoverflow.com/a/50097988/1163705 ). Меньше кодирования и исключает все догадки, алгоритмы и / или поиск существующих элементов.
Xandor

Ответы:

118

Обновление 24 августа 2012 г . : в jQuery 1.8 больше невозможно получить доступ к событиям элемента с помощью .data('events'). (Подробнее см. В этой ошибке .) Можно получить доступ к тем же данным с jQuery._data(elem, 'events')помощью внутренней структуры данных, которая недокументирована и, следовательно, не на 100% гарантирует стабильность. Однако это не должно быть проблемой, и соответствующую строку кода плагина выше можно изменить на следующее:

var data = jQuery._data(this[0], 'events')[type];

События jQuery хранятся в названном объекте данных events, поэтому вы можете искать в нем:

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

Конечно, было бы лучше, если бы вы могли структурировать свое приложение так, чтобы этот код вызывался только один раз.


Это может быть инкапсулировано в плагин:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

Затем вы можете позвонить:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
lonesomeday
источник
10
+1 за редактирование. Читая этот пост сегодня, я использую 1.8. Спасибо.
Gigi2m02
2
Спасибо за редактирование. Это только для кнопок или я тоже могу его использовать <a>? Потому что, когда я пытаюсь, он говорит о jQuery._data()следующей ошибкеTypeError: a is undefined
Houman
Я бы обернул строку var data = jQuery._data(this[0], 'events')[type];в try catch и вернул false в catch. Если никакие события не привязаны к этому [0], то вызов [type] вызовет некоторую вариацию «Невозможно получить свойство 'click' неопределенной или нулевой ссылки» и, очевидно, это также говорит вам то, что вам нужно знать.
Робб Вандавир
Я не уверен, изменился ли объект _data в jQuery 2.0.3, но я не мог использовать для этого $ .inArray. Функция, которую вы хотите сравнить, находится в свойстве каждого элемента данных, называемом «обработчик». Я изменил его, чтобы использовать простой оператор for, и проверил эквивалентность строк, что, как я полагаю, сделал inArray. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Робб Вандавир
почему бы не переместить ваше обновление / правку в начало? ... Это действительно правильный ответ.
Дон Чидл
180

Еще один способ - пометить такие кнопки классом CSS и фильтром:

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

В последних версиях jQuery замените bindна on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Конрад Гарус
источник
4
Превосходно! Спасибо, Конрад, это идеальный вариант. Мне нравятся такие элегантные и простые подходы. Я почти уверен, что он также более производительный, поскольку каждый отдельный элемент (к которому уже прикреплены обработчики событий щелчка) не должен выполнять полный поиск в некоторой большой корзине событий, сравнивая каждый из них. В своей реализации я назвал добавленный вспомогательный класс «привязанный к клику», который, как мне кажется, является более удобным вариантом: $ («. List-item: not (.click-bound)»). AddClass («привязанный к клику») ;
Николас Петерсен
1
Как было сказано в принятом ответе: «Было бы, конечно, лучше, если бы вы могли структурировать свое приложение так, чтобы этот код вызывался только один раз». Это и есть.
Джефф Дедж
+1 в моей ситуации off / on и bind / unbind предотвратил множественные звонки. Это было решение, которое сработало.
Джей
2
Это лучшее решение для производительности, потому что код может проверять наличие класса CSS и уже существующую привязку скольжения. Другое решение выполняет процесс отмены привязки, затем повторной привязки с (по крайней мере, насколько я могу судить) дополнительными накладными расходами.
Фил Николас
1
2 выпуска. (при использовании в подключаемом модуле, который может быть привязан к нескольким элементам) Не будет работать при привязке события изменения размера к объекту окна. Используйте data ('bound', 1) вместо addClass ('bound') - еще один подход, который работает. При уничтожении события он может сделать это, пока от него зависят другие экземпляры. Поэтому рекомендуется проверить, используются ли еще другие экземпляры.
Герберт Питерс,
59

При использовании jQuery 1.7+:

Вы можете позвонить offраньше on:

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

Если не:

Вы можете просто отвязать его первое событие:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Нафтали ака Нил
источник
1
Это не совсем прекрасное решение, но это будет улучшено только отменяя в один обработчик: .unbind('click', onButtonClicked). См. Руководство
lonesomeday
16
Фактически вы можете создавать пространства имен своих обработчиков по мере их добавления, тогда отмена привязки становится довольно простой. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday был ли в вашем примере использование unbind, чтобы показать что-то другое? Разве .unbind эффективно не то же самое, что .off, поэтому вам придется писать$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Джим
1
@Jim. Вы увидите, что вопрос был отредактирован через три года после моего комментария! (А также в протоколах сразу после моего комментария. Контент, который я комментировал, больше не существует, поэтому он, вероятно, не имеет большого смысла.)
lonesomeday
@lonesomeday хммм, я не уверен, почему он был отредактирован, как вы думаете, мне следует откатиться?
Нафтали, он же Нил,
8

На мой взгляд, лучший способ - использовать live () или delegate () для захвата события в родительском элементе, а не в каждом дочернем элементе.

Если ваша кнопка находится внутри элемента #parent, вы можете заменить:

$('#myButton').bind('click', onButtonClicked);

по

$('#parent').delegate('#myButton', 'click', onButtonClicked);

даже если #myButton еще не существует, когда этот код выполняется.

Pablonete
источник
2
Устаревшие методы
добс
8

Я написал очень маленький плагин под названием «once», который делает это. Выполнять от случая к случаю в элементе.

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

А просто:

$(element).once('click', function(){
});
Родольфо Хорхе Немер Ногейра
источник
9
.one () отбросит обработчик после первого выполнения.
Родольфо Хорхе Немер Ногейра
3
здесь «Once» означает прикрепление обработчика один раз. «один» в jquery предназначен для однократного запуска без однократного подключения.
Шишир Арора
1
Я знаю, что вмешиваюсь постфактум, но не offудаляю все обработчики для данного типа? Это означает, что если бы существовал отдельный обработчик кликов, не связанный, но с одним и тем же элементом, разве он не был бы удален?
Xandor
просто используйте пространство имен для события, чтобы оно не отбрасывало все обработчики вроде: 'keypup.test123'
SemanticZen
6

Почему бы не использовать это

unbind() перед bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
источник
1
$('#myButton').off().on('click', onButtonClicked);у меня тоже работает.
Veerakran Sereerungruangkul
У меня работает ... Решение gr8
imdadhusen
Прекрасный. У меня отлично работает для кнопки, которая уже была привязана.
seanbreeden
3

Вот моя версия:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

Использование:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)
Яир Галлер
источник
2

На основе ответа @ konrad-garus, но с использованием data, поскольку я считаю, что его classследует использовать в основном для стилизации.

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}
Ariel
источник
2

Чтобы избежать проверки / привязки / отмены привязки, вы можете изменить свой подход! Почему бы вам не использовать JQuery .on ()?

Поскольку Jquery 1.7, .live (), .delegate () устарели, теперь вы можете использовать .on () для

Прикрепите обработчик событий для всех элементов, которые соответствуют текущему селектору, сейчас и в будущем

Это означает, что вы можете прикрепить событие к родительскому элементу, который все еще существует, и присоединить дочерние элементы независимо от того, присутствуют они или нет!

Когда вы используете .on () вот так:

$('#Parent').on('click', '#myButton'  onButtonClicked);

Вы ловите событие, щелкнув по родительскому элементу, и ищите дочерний элемент '#myButton', если он существует ...

Поэтому, когда вы удаляете или добавляете дочерний элемент, вам не нужно беспокоиться о том, добавлять или удалять привязку события.

А. Морель
источник
Это абсолютно лучший ответ для текущих стандартов. Я не думаю, что эта функция настройки обработчика родительского элемента для наблюдения за ребенком была хорошо разрекламирована, но это довольно элегантное решение. У меня несколько раз происходил пожар, и каждый раз общее количество срабатываний продолжало увеличиваться. Эта единственная строка выполнила весь трюк и без использования .offкоторой, как я полагаю, удаляла бы несвязанные обработчики в процессе.
Xandor
1

Пытаться:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}
Мухтияр Замин
источник
0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}
Бишой ханна
источник
0

По состоянию на июнь 2019 года я обновил функцию (и она работает для того, что мне нужно)

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};
Карлос Казаликкио
источник
-2

У JQuery есть решение :

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

эквивалент:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});
Вениамин
источник
1
Ваш ответ не имеет отношения к вопросу. Вопрос касается привязки функции к событию элемента только один раз.
Michał Zalewski