4つのスクリプトブロックを含む可能性のあるページがあるWebアプリがあるとします-私が書くスクリプトはそれらのブロックの1つにあるかもしれませんが、どれがコントローラーによって処理されるのかわかりません。
一部のonclick
イベントをボタンにバインドしますが、予期しない順序で実行される場合があります。
注文を確実にする方法はありますか、または過去にこの問題をどのように処理しましたか?
4つのスクリプトブロックを含む可能性のあるページがあるWebアプリがあるとします-私が書くスクリプトはそれらのブロックの1つにあるかもしれませんが、どれがコントローラーによって処理されるのかわかりません。
一部のonclick
イベントをボタンにバインドしますが、予期しない順序で実行される場合があります。
注文を確実にする方法はありますか、または過去にこの問題をどのように処理しましたか?
回答:
私は古くからこの種のプロセスを一般化しようと試みていましたが、私の場合はチェーン内の最初のイベントリスナーの順序のみに関係していました。
それがなんらかの用途である場合、他の前に常にトリガーされるイベントリスナーをバインドする私のjQueryプラグインを次に示します。
** jQueryの変更に合わせて更新(thanks 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);
注意事項:
this.data("events")
ここでは、参照してくださいstackoverflow.com/questions/12214654/...
順序が重要な場合は、独自のイベントを作成し、それらのイベントが他のコールバックによってトリガーされたときに発生するコールバックをバインドできます。
$('#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;
});
少なくともそのようなもの。
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オブジェクトを使用して巧妙な処理を行うこともできます。
.delegate
はもう使用されておらず、使用する必要があります.on
が、同じ動作に到達する方法がわかりませんon
バインドされたコールバックが呼び出される順序は、各jQueryオブジェクトのイベントデータによって管理されます。データを直接表示および操作できる関数(私が知っている)はありません。bind()およびunbind()(または同等のヘルパー関数)のみを使用できます。
ダウスキーの方法が最適です。「最初の」コールバックが「実際の」イベントにバインドされた状態で、カスタムイベントの順序付けされたシーケンスにバインドするように、さまざまなバインドされたコールバックを変更する必要があります。そのように、それらがバインドされる順序に関係なく、シーケンスは正しい方法で実行されます。
私が見ることができる唯一の選択肢は、あなたが本当に熟考したくないものです:関数のバインディング構文があなたの前にバインドされている可能性があることがわかっている場合は、それらすべての関数のバインドを解除してから再度バインドしてみてください自分で正しい順序で。これで問題が発生します。コードが重複しているからです。
jQueryがオブジェクトのイベントデータ内のバインドされたイベントの順序を変更することを許可しただけで、jQueryコアにフックするコードを記述せずに可能であると思わない場合は、すばらしいでしょう。そして、おそらくこれを許可することには私が考えていなかった影響があるので、意図的に省略しているのかもしれません。
jQueryユニバースでは、バージョン1.8とは異なる方法で実装する必要があることに注意してください。次のリリースノートはjQueryブログからのものです。
.data(“ events”):jQueryは、そのイベント関連データを、各要素のイベントという名前の(それを待つ)データオブジェクトに格納します。これは内部データ構造であるため、1.8ではユーザーデータ名前空間から削除されるため、同じ名前のアイテムと競合しません。jQueryのイベントデータは、jQuery._data(element、 "events")を介して引き続きアクセスできます。
ハンドラーがjQueryユニバースで実行される順序を完全に制御できます。Ricooはこれを上で指摘しています。彼の答えが彼に多くの愛情を与えたようには見えませんが、このテクニックは非常に便利です。たとえば、ライブラリウィジェットのハンドラーの前に独自のハンドラーを実行する必要がある場合、またはウィジェットのハンドラーへの呼び出しを条件付きでキャンセルする権限が必要な場合はいつでも検討してください。
$("button").click(function(e){
if(bSomeConditional)
e.stopImmediatePropagation();//Don't execute the widget's handler
}).each(function () {
var aClickListeners = $._data(this, "events").click;
aClickListeners.reverse();
});
ハンドラを通常どおりにバインドして実行します。
element.data('events').action.reverse();
だから例えば:
$('#mydiv').data('events').click.reverse();
$._data(element, 'events')[name].reverse()
動作します
あなたはこのようなことを試すことができます:
/**
* 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]);
}
}
同じ問題があり、このトピックが見つかりました。上記の答えはそれらの問題を解決することができますが、私はそれらが良い計画だとは思いません。
現実の世界について考えてみましょう。
これらの回答を使用する場合は、コードを変更する必要があります。コードスタイルを変更する必要があります。このようなもの:
元の:
$('form').submit(handle);
ハック:
bindAtTheStart($('form'),'submit',handle);
時間の経過とともに、プロジェクトについて考えます。コードは醜くて読みにくいです!anthoerの理由は単純ですが常に優れています。bindAtTheStartが10個ある場合は、バグがない可能性があります。bindAtTheStartが100ある場合、それらを正しい順序で保持できると本当に確信していますか?
同じイベントを複数バインドする必要がある場合は、js-fileまたはjs-codeの読み込み順序を制御するのが最善の方法だと思います。jqueryはイベントデータをキューとして処理できます。順序は先入れ先出しです。コードを変更する必要はありません。ロード順序を変更するだけです。
これが私のショットです。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;
}
});
}
});
いくつかの特殊なケースでは、クリックイベントのバインド方法を変更できず(イベントバインディングは他のコードから作成されます)、HTML要素を変更できる場合、考えられる解決策を次に示します(警告:これは推奨されるバインド方法ではありません)イベント、他の開発者はこれであなたを殺害するかもしれません):
<span onclick="yourEventHandler(event)">Button</span>
このバインド方法では、イベントハンドラーが最初に追加されるため、最初に実行されます。
jQuery 1.5はpromiseを導入しています。実行順序を制御するために私が見た中で最も簡単な実装を次に示します。http://api.jquery.com/jquery.when/にある完全なドキュメント
$.when( $('#myDiv').css('background-color', 'red') )
.then( alert('hi!') )
.then( myClickFunction( $('#myID') ) )
.then( myThingToRunAfterClick() );