Как заказать события, связанные с jQuery

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

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

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

Ответ 1

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

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

** ОБНОВЛЕНО inline с изменениями 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() и других связанных функций.

Ответ 2

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

$('#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;
});

Что-то вроде этого как минимум.

Ответ 3

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

Если вы хотите, чтобы обратные вызовы были независимыми друг от друга, вы могли бы воспользоваться преимуществами пузырьков и присоединить последующие события в качестве делегатов к родительским элементам. Обработчики на родительских элементах будут запускаться после обработчиков на элементе, продолжая вплоть до документа. Это неплохо, так как вы можете использовать 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, вы также можете сделать что-то умное с новым объектом Deferred.

Ответ 4

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

Метод Dowski лучше всего, вы должны изменить различные связанные обратные вызовы для привязки к упорядоченной последовательности пользовательских событий, причем "первый" обратный вызов привязан к "реальному" событию. Таким образом, независимо от того, в каком порядке они связаны, последовательность будет выполняться правильным образом.

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

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

Ответ 5

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

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

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

$("button").click(function(e){
    if(bSomeConditional)
       e.stopImmediatePropagation();//Don't execute the widget handler
}).each(function () {
    var aClickListeners = $._data(this, "events").click;
    aClickListeners.reverse();
});

Ответ 6

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;
}

Ответ 7

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

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

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

$('#mydiv').data('events').click.reverse();

Ответ 8

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

/**
  * 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]);
    }   
}

Ответ 9

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

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

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

оригинал:

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

хак:

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

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

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

Ответ 10

Здесь мой снимок, охватывающий разные версии jQuery:

// Binds a jQuery event to elements at the start of the event chain for that type.
jQuery.extend({
    _bindEventHandlerAtStart: function ($elements, eventType, handler) {
        var _data;

        $elements.bind(eventType, handler);
        // This bound the event, naturally, at the end of the event chain. We
        // need it at the start.

        if (typeof jQuery._data === 'function') {
            // Since jQuery 1.8.1, it seems, that the events object isn't
            // available through the public API `.data` method.
            // Using `$._data, where it exists, seems to work.
            _data = true;
        }

        $elements.each(function (index, element) {
            var events;

            if (_data) {
                events = jQuery._data(element, 'events')[eventType];
            } else {
                events = jQuery(element).data('events')[eventType];
            }

            events.unshift(events.pop());

            if (_data) {
                jQuery._data(element, 'events')[eventType] = events;
            } else {
                jQuery(element).data('events')[eventType] = events;
            }
        });
    }
});

Ответ 11

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

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

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

Ответ 12

JQuery 1.5 вводит promises, и здесь простейшая реализация, которую я видел, контролирует порядок выполнения. Полная документация на http://api.jquery.com/jquery.when/

$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );