web-dev-qa-db-ja.com

最初にトリガーされるように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を使用していますが、必要に応じてアップグレードします。

36
Jacob

ここに私がしばらく前にやった簡単なプラグインがあります。ハンドラーをリストの先頭にバインドできます。それは非常に単純であり、名前空間付きのイベントやひどく派手なもので動作することを保証しません。

単一またはスペースで区切られたイベントのグループを単純にバインドする場合は、機能するはずです。

例: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
14
user113716

ネイティブのonbinddelegateおよびliveメソッドは、イベントを登録キューの先頭にプッシュします。また、1.7と以前のバージョン間のイベント登録の違いも考慮に入れます。使用方法は次のとおりです。

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

これらのほとんどの回答と同様に、大きな欠点は、jQueryの内部イベント登録ロジックに依存しているため、バージョン1.7のように、変更すると簡単に壊れてしまう可能性があることです。プロジェクトの寿命を延ばすために、jQueryの内部をハイジャックすることを含まないソリューションを見つける方が良い場合があります。

私の特定のケースでは、ニースをプレイするために2つのプラグインを取得しようとしていました。 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;
8
Jordan Gray

ここで答えたように https://stackoverflow.com/a/35472362/1815779 の場合、次のようにすることができます:

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

警告:これはではありませんイベントをバインドする推奨方法、他の開発者これであなたを殺すかもしれません。

3
Linh Dam

ハンドラー、ネームスペース、非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);
        }
    });
};
1
roktir

@patrick:私は同じ問題を解決しようとしてきましたが、この解決策はまさに私が必要としていることを行います。マイナーな問題の1つは、プラグインが新しいイベントのネームスペースを処理しないことです。このマイナーなTweakがそれを処理します:

変化する:

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

に:

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

選択した回答に加えて、パラメータが不足していると考えてください:

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
jLuna

これはどうですか?イベントをバインドし、これを実行します。

handlers.unshift( handlers.pop() );
1
phoenix12

非常にシンプルな関数を作成して、クリックイベントハンドラーを最初の位置に配置します。既存のすべてのクリックハンドラーは手動でのみトリガーされます。

$.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
Charlie

私の最善の試み。
次のような構造のコードがありました。

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
onassar