Модель событий JQuery и предотвращение дублирования обработчиков - PullRequest
32 голосов
/ 02 февраля 2010

Еще раз я хочу загрузить страницу, которая содержит собственный скрипт, в div, используя $ ("divid"). Load (...). Проблема, с которой я сталкиваюсь, связана с событиями. Допустим, мы запускаем («обезьяна») с родительской страницы и на загруженной странице связываем («обезьяна») и просто делаем предупреждение («привязано обезьяной»). Если один и тот же метод загрузки вызывается несколько раз, привязка вызывается несколько раз. Теперь я могу просто отменить привязку перед привязкой или проверить количество обработчиков перед привязкой, а затем не связывать ее, чтобы предотвратить это. Ни одна из этих опций не является масштабируемой, как если бы я позже захотел привязать этот триггер на другой «подстранице» (странице, загруженной в div).

Что я в идеале хочу сделать, так это проверить, существует ли уже обработчик, который я собираюсь добавить, но я все еще ХОЧУ использовать анонимные обработчики ... (я думаю, что с этим последним запросом я немного). В настоящее время у меня есть обходной путь, используя предварительно определенные / именованные методы, а затем проверяя это перед привязкой.

// Found this on StackOverflow
function getFunctionName(fn)
{
 var rgx = /^function\s+([^\(\s]+)/
 var matches = rgx.exec(fn.toString());
 return matches ? matches[1] : "(anonymous)"
}

function HandlerExists(triggerName, handlerName) {
        exists = false;
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if(event !== undefined)
            {
                $.each(event, function(i, handler) {
                    alert(handlerName);
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }

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

if (!HandlerExists("test", "theMethod")) {
    $(document).bind("test", theMethod);
}

У кого-нибудь есть более элегантное решение? например, есть ли способ проверить, загружен ли конкретный скрипт? так что я мог бы использовать getScript (), чтобы загрузить js из дочерней страницы при первой загрузке, а затем просто не загружать ее при последующих загрузках (и просто запустить триггер, который будет обрабатываться ранее существовавшим js).

Ответы [ 5 ]

50 голосов
/ 20 ноября 2012

Предотвращение дублирования привязки с использованием пространства имен события jQuery

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

Это общая проблема с событиями. Поэтому я немного объясню события jQuery и использование пространства имен для предотвращения дублирования привязок.



ОТВЕТ: (Короткий и прямой к точке)


// bind handler normally
$('#myElement').bind('myEvent', myMainHandler);

// bind another using namespace
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// unbind the unique and rebind the unique
$('#myElement').unbind('myEvent.myNamespace').bind('myEvent.myNamespace', myUniqueHandler);
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// trigger event
$('#myElement').trigger('myEvent');

// output
myMainHandler() // fires once!
myUniqueHandler() // fires once!



ПРИМЕР ОТВЕТА: (Полное подробное объяснение)


Сначала давайте создадим пример элемента для привязки. Мы будем использовать кнопку с идентификатором #button. Затем создайте 3 функции, которые могут и будут использоваться в качестве обработчиков для привязки к событию:

функция exampleOne () мы будем связывать одним кликом. функцию exampleTwo () мы будем привязывать к пространству имен клика. Функция exampleThree () мы будем привязывать к пространству имен щелчка, но отменять привязку и привязывать несколько раз, не удаляя другие привязки, что предотвращает дублирование привязки, не удаляя при этом другие связанные методы.

Пример запуска: (Создать элемент для привязки и некоторые методы для наших обработчиков)

<button id="button">click me!</button>


// create the example methods for our handlers
function exampleOne(){ alert('One fired!'); }
function exampleTwo(){ alert('Two fired!'); }
function exampleThree(){ alert('Three fired!'); }

Привязать примерНажмите, чтобы нажать:

$('#button').bind('click', exampleOne); // bind example one to "click" 

Теперь, если пользователь нажимает кнопку или вызывает $ ('кнопка #)'. Trigger ('click'), вы получите предупреждение "One Fired!";

Привязать пример два к пространству имен щелчка:"имя произвольно, мы будем использовать myNamespace2"

$('#button').bind('click.myNamespace2', exampleTwo);

Крутая вещь в этом состоит в том, что мы можем вызвать «щелчок», который будет запускать exampleOne () И exampleTwo (), или мы можем вызвать «click.myNamespace2», который будет запускать только exampleTwo ()

Привязать exampleThree к пространству имен click:"снова, имя произвольно, если оно отличается от пространства имен exampleTwo, мы будем использовать myNamespace3"

$('#button').bind('click.myNamespace3', exampleThree);

Теперь, если сработает 'click' get, ВСЕ три примера метода будут запущены, или мы сможем нацелиться на определенное пространство имен.

ВСЕ ВМЕСТЕ, ЧТОБЫ ПРЕДОТВРАТИТЬ ДУБЛИКАТ

Если бы мы продолжали связывать exampleThree () примерно так:

$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').bind('click.myNamespace3', exampleThree);

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

$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);

Если активирована функция щелчка, exampleOne (), exampleTwo () и exampleThree () запускаются только один раз.

Чтобы обернуть все это в простую функцию:

var myClickBinding = function(jqEle, handler, namespace){
    if(namespace == undefined){
        jqEle.bind('click', handler);
    }else{
        jqEle.unbind('click.'+namespace).bind('click.'+namespace, handler);
    }
}   

Резюме:

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

Для дальнейшего объяснения: http://api.jquery.com/event.namespace/

23 голосов
/ 11 марта 2014

Отличный ответ от

событие связывания только один раз

copyPastedInfo

если вы можете применить его, вероятно, захотите взглянуть на event.preventDefaultt и event.stopPropagation ИЛИ отсоединяйте и связывайте каждый раз в рамках метода, подобного

function someMethod()
{
  $(obj).off('click').on('click', function {});
}
7 голосов
/ 02 февраля 2010

Ммм, как насчет использования one()? http://api.jquery.com/one/

Или я совершенно не понимаю тебя?

6 голосов
/ 02 февраля 2010

Это не полный ответ, но я быстро нашел, как исправить весь мой код с минимальными изменениями. В моем «базовом» классе js (это объект, который используется на каждой странице для стандартных соединений кнопок по имени класса и т. Д.), Я добавил следующий метод, который выполняет проверку для обработчиков дублирования:

BindOnce: function(triggerName, fn) {
    function handlerExists(triggerName, theHandler) {
        function getFunctionName(fn) {
            var rgx = /^function\s+([^\(\s]+)/
            var matches = rgx.exec(fn.toString());
            return matches ? matches[1] : "(anonymous)"
        }
        exists = false;
        var handlerName = getFunctionName(theHandler);
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if (event !== undefined) {
                $.each(event, function(i, handler) {
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }
    if (!handlerExists(triggerName, fn)) {
        $(document).bind(triggerName, fn);
    }
},

Тогда я просто вызываю его вместо метода связывания!

$.mystuff.BindOnce("TheTrigger", OnTriggered)

Обратите внимание, что здесь нельзя использовать anon-методы, так как они будут просто вызываться 1, 2, 3 и т. Д., И единственный способ проверить наличие дупликов - использовать метод toString (), что будет довольно медленно комплексное применение

1 голос
/ 14 июня 2011

Слишком поздно на сто лет, но если вы не используете анонимные функции, вы можете сделать это намного проще, просто сначала отмените привязку:

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
        var oneHandler = (function () {
             HANDLER CODE
        });

    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

Эта реализация будет работать в Firefox 4, но не во многих других браузерах - потому что переменная-обработчик создается каждый раз как новая, поэтому при откреплении не удается найти соответствующий обработчик для открепления Перемещение обработчика в родительскую область решает проблему: т.е.

var oneHandler = (function () {
    HANDLER CODE
});

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

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

...