Подтвердить что ты не робот

Присоединение обработчиков событий jQuery, чтобы они запускались сначала

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

$.fn.extend({
    bindFirst: function(type, handler) {

        var baseType = type;
        var dotIdx = type.indexOf('.');
        if (dotIdx >= 0) {
            baseType = type.substr(0, dotIdx);
        }

        this.each(function() {
            var oldEvts = {};
            var data = $.data(this);
            var events = data.events || data.__events__;
            var handlers = events[baseType];
            for (var h in handlers) {
                if (handlers.hasOwnProperty(h)) {
                    oldEvts[h] = handlers[h];
                    delete handlers[h];
                    // Also tried an unbind here, to no avail
                }
            }

            var self = $(this);
            self.bind(type, handler);

            for (var h in oldEvts) {
                if (oldEvts.hasOwnProperty(h)) {
                    self.bind(baseType, oldEvts[h]);
                }
            }
        });
    }
});

Есть ли естественный способ изменить порядок обработки событий? Если нет, знаете ли вы, какую технику я мог бы применить? Я использую jQuery 1.4.1, хотя я обновляюсь, если должен.

4b9b3361

Ответ 1

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

Для просто связывания отдельной или отдельной отдельной группы событий она должна работать.

Пример: http://jsfiddle.net/gbcUy/

$.fn.bindUp = function(type, fn) {

    type = type.split(/\s+/);

    this.each(function() {
        var len = type.length;
        while( len-- ) {
            $(this).bind(type[len], fn);

            var evt = $.data(this, 'events')[type[len]];
            evt.splice(0, 0, evt.pop());
        }
    });
};

Или, если вы хотите каким-то другим образом манипулировать массивом обработчиков, просто получите обработчики для элемента, который вы хотите, и манипулируйте им, но вы хотите:

Пример: http://jsfiddle.net/gbcUy/1/

var clickHandlers = $('img').data('events').click;

clickHandlers.reverse(); // reverse the order of the Array

Ответ 2

Существует довольно приятный плагин под названием jQuery.bind-first, который предоставляет аналоги натурных on, bind, delegate и live методы, которые вызывают событие в верхней части очереди регистрации. Он также учитывает различия в регистрации событий между версиями от 1.7 до более ранних версий. Здесь как его использовать:

$('button')
    .on     ('click', function() { /* Runs second */ })
    .onFirst('click', function() { /* Runs first  */ });

Как и в большинстве этих ответов, большой недостаток заключается в том, что он опирается на внутреннюю логику регистрации событий jQuery и может легко сломаться, если он изменится, как это было в версии 1.7! Возможно, было бы лучше для долговечности вашего проекта найти решение, которое не связано с захватом внутренних компонентов jQuery.

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

$('button')
    .on('click', function() {
        // Declare and trigger a "before-click" event.
        $(this).trigger('before-click');

        // Subsequent code run after the "before-click" events.
    })
    .on('before-click', function() {
        // Run before the main body of the click event.
    });

И, в случае необходимости, здесь, как установить свойства объекта события, переданные функции обработчика, и получить результат последнего события before-click для выполнения:

// Add the click event pageX and pageY to the before-click event properties.
var beforeClickEvent = $.Event('before-click', { pageX: e.pageX, pageY: e.pageY });
$(this).trigger(beforeClickEvent);

// beforeClickEvent.result holds the return value of the last before-click event.
if (beforeClickEvent.result === 'no-click') return;

Ответ 3

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

Изменить:

var evt = $.data(this, 'events')[type[len]];

в

var evt = $.data(this, 'events')[type[len].replace(/\..+$/, '')];

Ответ 4

как насчет этого? связать событие и сделать это:

handlers.unshift( handlers.pop() );

Ответ 5

В дополнение к выбранному ответу, рассмотрите недостающие параметры:

jQuery.fn.bindUp = function (type, parameters, fn) {
    type = type.split(/\s+/);

    this.each(function () {
        var len = type.length;
        while (len--) {
            if (typeof parameters === "function")
                jQuery(this).bind(type[len], parameters);
            else
                jQuery(this).bind(type[len], parameters, fn);

            var evt = jQuery._data(this, 'events')[type[len]];
            evt.splice(0, 0, evt.pop());
        }
    });
};

Ответ 6

Как указано здесь fooobar.com/questions/31134/..., вы можете сделать вот так:

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

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

Ответ 7

Здесь используется комбинация некоторых предыдущих методов, включая поддержку обработчиков, пространств имен, привязок без jquery и поддержки:

$.fn.oneFirst = function(event_type, event_callback, handler) {
    return this.bindFirst(event_type, event_callback, handler, "one");
},
$.fn.bindFirst = function(event_type, event_callback, handler, bind_type) {
    var event_types = event_type.split(/\s+/);
    var pos;
    handler = (handler == undefined ? event_callback : handler);
    event_callback = (typeof event_callback == "function" ? {} : event_callback);

    this.each(function() {
        var $this = $(this);
        for (var i in event_types) { // each bound type
            event_type = event_types[i];

            var event_namespace = ((pos = event_type.indexOf(".")) > 0 ? event_type.substring(pos) : "");
            event_type = (pos > 0 ? event_type.substring(0, pos) : event_type);
            var current_attr_listener = this["on" + event_type];

            if (current_attr_listener) { // support non-jquery binded events
                $this.bind(event_type, function(e) {
                    return current_attr_listener(e.originalEvent);
                });
                this["on" + event_type] = null;
            }

            if (bind_type == "one") {
                $this.one(event_type + event_namespace, event_callback, handler);
            }
            else {
                $this.bind(event_type + event_namespace, event_callback, handler);
            }

            var all_events = $.data(this, 'events') || $._data(this).events;
            var type_events = all_events[event_type];
            var new_event = type_events.pop();
            type_events.unshift(new_event);
        }
    });
};

Ответ 8

Моя лучшая попытка
У меня был код, который был структурирован следующим образом:

var $body = jQuery('body');
$body.on({
    'click': function(event){
    }
});

Чтобы затем убедиться, что обратный вызов был первым, я использовал эту функцию:

/**
 * promoteLastEvent
 * 
 * @access  public
 * @param   jQuery $element
 * @param   String eventName
 * @return  void
 */
function promoteLastEvent($element, eventName) {
    var events = jQuery._data($element.get(0), 'events'),
        eventNameEvents = events[eventName],
        lastEvent = eventNameEvents.pop();
    eventNameEvents.splice(1, 0, lastEvent);
};

Это называется следующим образом:

promoteLastEvent($body, 'click');

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

Ответ 9

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

$.fn.bindClickFirst = function (eventHandler) {
    var events = $._data($next[0], "events");
    var clickEvtHandlers = [];

    events.click.forEach(function (evt) {
        clickEvtHandlers.push(evt.handler);
    });
    $next.off("click");

    $next.on("click", function () {
        var evtArg = event;
        eventHandler(evtArg, function () {
            clickEvtHandlers.forEach(function (evt) {
                evt(evtArg);
            });
        });
    })
}

И использовать функцию:

$btn.bindClickFirst(function (evt, next) {
    setTimeout(function () {
        console.log("bbb");
        next();
    }, 200)
})