Модель события JQuery и предотвращение дублирования обработчиков

Еще раз хочу загрузить страницу, содержащую ее script, в div, используя $( "div" ). 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);
}

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

Ответ 1

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

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(), который будет довольно медленным в сложное приложение

Ответ 2

Предотвращение дублирования привязки с использованием пространства имен событий 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 функции, которые могут и будут использоваться в качестве обработчиков для привязки к событию:

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

Пример Start: (Создайте элемент для привязки, а некоторые методы - наши обработчики)

<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" 

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

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

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

Приятная вещь в этом, мы можем запустить "click", который будет запускать exampleOne() AND exampleTwo(), или мы можем вызвать "click.myNamespace2", который будет запускать только exampleTwo()

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

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

Теперь, если 'click' получит триггер, будут задействованы все три примера методов, или мы можем настроить таргетинг на определенное пространство имен.

ОТПРАВИТЬ ЭТО ВСЕ ВМЕСТЕ, ЧТОБЫ ПРЕДОТВРАТИТЬ DUPLICATE

Если бы мы продолжали связывать 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);

Если функция click активирована, 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/

Ответ 4

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

Или я полностью недопонимаю вас?

Ответ 5

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

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

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

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

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

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

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