Присоединение обработчиков событий jQuery, чтобы они запускались первыми - PullRequest
33 голосов
/ 20 января 2011

Есть ли способ присоединить обработчик событий 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, хотя я буду обновлять, если мне нужно.

Ответы [ 9 ]

14 голосов
/ 20 января 2011

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

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

Пример: 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
7 голосов
/ 19 ноября 2013

Существует довольно хороший плагин под названием 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's 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;
1 голос
/ 18 февраля 2016

Как ответили здесь https://stackoverflow.com/a/35472362/1815779,, вы можете сделать так:

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

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

1 голос
/ 06 января 2015

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

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());
        }
    });
};
1 голос
/ 22 марта 2013

что по этому поводу?связать событие и чем это сделать:

handlers.unshift( handlers.pop() );
1 голос
/ 26 июля 2011

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

Изменение:

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

до:

var evt = $.data(this, 'events')[type[len].replace(/\..+$/, '')];
0 голосов
/ 15 мая 2019

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

$.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)
})
0 голосов
/ 07 марта 2019

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

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.

0 голосов
/ 20 декабря 2013

Вот комбинация некоторых предыдущих методов, включая поддержку обработчиков, пространства имен, привязок не-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);
        }
    });
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...