Как заказать события, связанные с JQuery - PullRequest
161 голосов
/ 14 ноября 2008

Допустим, у меня есть веб-приложение, в котором есть страница, которая может содержать 4 блока скриптов - скрипт, который я пишу, может быть найден в одном из этих блоков, но я не знаю, какой из них обрабатывается контроллером.

Я связываю некоторые onclick события с кнопкой, но обнаруживаю, что они иногда выполняются в порядке, которого я не ожидал.

Есть ли способ обеспечить порядок или как вы решали эту проблему в прошлом?

Ответы [ 12 ]

134 голосов
/ 14 ноября 2008

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

$('#mydiv').click(function(e) {
    // maniplate #mydiv ...
    $('#mydiv').trigger('mydiv-manipulated');
});

$('#mydiv').bind('mydiv-manipulated', function(e) {
    // do more stuff now that #mydiv has been manipulated
    return;
});

Нечто подобное, по крайней мере.

37 голосов
/ 02 апреля 2011

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

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

$( '#mybutton' ).click( function(e) { 
    // Do stuff first
} );

$( '#mybutton' ).click( function(e) { 
    // Do other stuff first
} );

$( document ).delegate( '#mybutton', 'click', function(e) {
    // Do stuff last
} );

Или, если вам это не нравится, вы можете использовать плагин Nick Leaches bindLast, чтобы событие было привязано последним: https://github.com/nickyleach/jQuery.bindLast.

Или, если вы используете jQuery 1.5, вы также можете сделать что-то умное с новым отложенным объектом.

27 голосов
/ 27 мая 2011

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

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

** ОБНОВЛЕНО в линию с изменениями jQuery (спасибо Toskan) **

(function($) {
    $.fn.bindFirst = function(/*String*/ eventType, /*[Object])*/ eventData, /*Function*/ handler) {
        var indexOfDot = eventType.indexOf(".");
        var eventNameSpace = indexOfDot > 0 ? eventType.substring(indexOfDot) : "";

        eventType = indexOfDot > 0 ? eventType.substring(0, indexOfDot) : eventType;
        handler = handler == undefined ? eventData : handler;
        eventData = typeof eventData == "function" ? {} : eventData;

        return this.each(function() {
            var $this = $(this);
            var currentAttrListener = this["on" + eventType];

            if (currentAttrListener) {
                $this.bind(eventType, function(e) {
                    return currentAttrListener(e.originalEvent); 
                });

                this["on" + eventType] = null;
            }

            $this.bind(eventType + eventNameSpace, eventData, handler);

            var allEvents = $this.data("events") || $._data($this[0], "events");
            var typeEvents = allEvents[eventType];
            var newEvent = typeEvents.pop();
            typeEvents.unshift(newEvent);
        });
    };
})(jQuery);

На заметку:

  • Это не было полностью проверено.
  • Он опирается на то, что внутренняя структура фреймворка jQuery не меняется (тестируется только с 1.5.2).
  • Он не обязательно будет запущен перед слушателями событий, которые связаны каким-либо образом, кроме как в качестве атрибута исходного элемента или с использованием jQuery bind () и других связанных функций.
15 голосов
/ 14 ноября 2008

Порядок вызова связанных обратных вызовов управляется данными события каждого объекта jQuery. Нет никаких функций (о которых я знаю), которые бы позволяли вам просматривать и манипулировать этими данными напрямую, вы можете использовать только bind () и unbind () (или любую из эквивалентных вспомогательных функций).

Лучше всего использовать метод Дауски, вам следует изменить различные связанные обратные вызовы, чтобы связать их с упорядоченной последовательностью пользовательских событий, при этом «первый» обратный вызов связан с «реальным» событием. Таким образом, независимо от того, в каком порядке они связаны, последовательность будет выполняться правильно.

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

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

8 голосов
/ 07 октября 2013

Обратите внимание, что во вселенной jQuery это должно быть реализовано по-другому, начиная с версии 1.8. Следующая заметка о выпуске взята из блога jQuery:

.data («события»): jQuery сохраняет свои связанные с событием данные в объекте данных именованные (подождите) события на каждом элементе. Это внутренние данные структура так в 1.8 это будет удалено из пространства имен пользовательских данных поэтому он не будет конфликтовать с предметами с таким же именем. Данные о событиях jQuery все еще можно получить доступ через jQuery._data (элемент, "события")

Мы полностью контролируем порядок, в котором обработчики будут выполнять во вселенной jQuery . Рику указывает на это выше. Не похоже, что его ответ принес ему много любви, но эта техника очень удобна. Рассмотрим, например, всякий раз, когда вам нужно выполнить собственный обработчик до какого-либо обработчика в виджете библиотеки, или вам нужно иметь возможность условно отменить вызов обработчика виджета:

$("button").click(function(e){
    if(bSomeConditional)
       e.stopImmediatePropagation();//Don't execute the widget's handler
}).each(function () {
    var aClickListeners = $._data(this, "events").click;
    aClickListeners.reverse();
});
7 голосов
/ 15 января 2011
function bindFirst(owner, event, handler) {
    owner.unbind(event, handler);
    owner.bind(event, handler);

    var events = owner.data('events')[event];
    events.unshift(events.pop());

    owner.data('events')[event] = events;
}
6 голосов
/ 12 декабря 2011

просто связать обработчик нормально и затем запустить:

element.data('events').action.reverse();

так например:

$('#mydiv').data('events').click.reverse();
3 голосов
/ 12 января 2011

Вы можете попробовать что-то вроде этого:

/**
  * Guarantee that a event handler allways be the last to execute
  * @param owner The jquery object with any others events handlers $(selector)
  * @param event The event descriptor like 'click'
  * @param handler The event handler to be executed allways at the end.
**/
function bindAtTheEnd(owner,event,handler){
    var aux=function(){owner.unbind(event,handler);owner.bind(event,handler);};
    bindAtTheStart(owner,event,aux,true);

}
/**
  * Bind a event handler at the start of all others events handlers.
  * @param owner Jquery object with any others events handlers $(selector);
  * @param event The event descriptor for example 'click';
  * @param handler The event handler to bind at the start.
  * @param one If the function only be executed once.
**/
function bindAtTheStart(owner,event,handler,one){
    var eventos,index;
    var handlers=new Array();
    owner.unbind(event,handler);
    eventos=owner.data("events")[event];
    for(index=0;index<eventos.length;index+=1){
        handlers[index]=eventos[index];
    }
    owner.unbind(event);
    if(one){
        owner.one(event,handler);
    }
    else{
        owner.bind(event,handler);
    }
    for(index=0;index<handlers.length;index+=1){
        owner.bind(event,ownerhandlers[index]);
    }   
}
1 голос
/ 15 июня 2016

У меня такая же проблема, и я нашел эту тему. Приведенные выше ответы могут решить эту проблему, но я не думаю, что это хорошие планы.

давайте подумаем о реальном мире.

если мы используем эти ответы, мы должны изменить наш код. Вы должны изменить свой стиль кода. как то так:

оригинал:

$('form').submit(handle);

хак:

bindAtTheStart($('form'),'submit',handle);

со временем подумай о своем проекте. код ужасен и труден для чтения! Другая причина проста, всегда лучше. если у вас есть 10 bindAtTheStart, возможно, нет ошибок. если у вас есть 100 bindAtTheStart, действительно ли вы уверены, что можете держать их в правильном порядке?

так что если вам нужно связать несколько событий одинаково. Я думаю, что лучший способ - это контролировать порядок загрузки js-файла или js-кода JQuery может обрабатывать данные события как очередь. заказ первым пришел, первым вышел. вам не нужно менять код. просто измените порядок загрузки.

0 голосов
/ 18 февраля 2016

В некоторых особых случаях, когда вы не можете изменить способ привязки событий щелчка (привязки событий сделаны из чужих кодов), и вы можете изменить элемент HTML, вот возможное решение ( предупреждение : это не рекомендуемый способ связывания событий, другие разработчики могут убить вас за это):

<span onclick="yourEventHandler(event)">Button</span>

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...