附加jQuery事件处理程序,以便首先触发它们

时间:2011-01-20 01:24:49

标签: jquery events binding

有没有办法附加一个jQuery事件处理程序,以便在任何以前附加的事件处理程序之前触发处理程序?我遇到this article,但代码不起作用,因为事件处理程序不再存储在数组中,这是他的代码所期望的。我试图创建一个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 个答案:

答案 0 :(得分:14)

这是我做了一段时间的简单插件。允许您将处理程序绑定到列表的开头。它非常简单,我不保证它适用于命名空间事件或任何非常奇特的事情。

对于简单地绑定单个或空格的单独事件组,它应该可以工作。

示例: 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

答案 1 :(得分:7)

有一个名为jQuery.bind-first的相当不错的插件,它提供了将事件推送到的原生onbinddelegatelive方法的类似物注册队列的顶部。它还考虑了1.7和早期版本之间事件注册的差异。以下是如何使用它:

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

与大多数这些答案一样,最大的缺点是它依赖于jQuery的内部事件注册逻辑,并且如果它发生变化就很容易破坏 - 就像它在1.7版本中那样!对于项目的寿命来说,找到一个不涉及劫持jQuery内部的解决方案可能会更好。

在我的特定情况下,我试图让两个插件玩得很好。我使用the trigger method文档中描述的自定义事件来处理它。您可以根据自己的情况采用类似的方法。这是一个让你入门的例子:

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

答案 2 :(得分:1)

@patrick:我一直在努力解决同样的问题,这个解决方案完全符合我的需要。一个小问题是您的插件不处理新事件的命名空间。这个小调整应该照顾它:

变化:

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

为:

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

答案 3 :(得分:1)

这是怎么回事? 绑定事件,然后执行此操作:

handlers.unshift( handlers.pop() );

答案 4 :(得分:1)

除了选定的答案外,请考虑缺少参数:

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

答案 5 :(得分:1)

正如https://stackoverflow.com/a/35472362/1815779所述,您可以这样做:

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

警告:这是绑定事件的推荐方式,其他开发者可能会因此而谋杀你。

答案 6 :(得分:0)

以下是一些先前方法的组合,包括对处理程序,命名空间,非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);
        }
    });
};

答案 7 :(得分:0)

我最好的尝试。
我的代码结构如下:

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,它对我来说效果很好。

答案 8 :(得分:0)

我创建了一个非常简单的函数,将点击事件处理程序置于第一个位置,并且所有现有的点击处理程序都只会手动触发。

$.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)
})