クリックイベントが既にバインドされているかどうかを確認する方法-JQuery


130

クリックイベントをボタンにバインドしています。

$('#myButton').bind('click',  onButtonClicked);

1つのシナリオでは、これが複数回呼び出されているため、実行するtriggerと、防止したい複数のajax呼び出しが表示されます。

bind以前にバインドされていない場合にのみどうすればよいですか。


1
+ Konrad Garusが最も安全なアンサー(stackoverflow.com/a/6930078/842768)を持っていると思います。受け入れた回答を変更することを検討してください。乾杯!更新:+ Herbert Petersのコメントも確認してください!それが最善のアプローチです。
giovannipds

現在の標準については、以下の@A Morelの回答を参照してください(stackoverflow.com/a/50097988/1163705)。コーディングが少なく、すべての推測作業、アルゴリズム、および/または既存の要素の検索を方程式から除外します。
Xandor

回答:


117

12年8月24日更新:jQuery 1.8では、を使用して要素のイベントにアクセスすることはできなくなりました.data('events')。(詳細については、このバグを参照してください。)jQuery._data(elem, 'events')内部データ構造であるを使用して同じデータにアクセスすることは可能ですが、これは文書化されていないため、100%安定していることが保証されていません。ただし、これは問題にならず、上記のプラグインコードの関連する行を次のように変更できます。

var data = jQuery._data(this[0], 'events')[type];

jQueryイベントはと呼ばれるデータオブジェクトに格納されるeventsため、次のように検索できます。

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

もちろん、このコードが1回だけ呼び出されるようにアプリケーションを構成できれば最適です。


これはプラグインにカプセル化できます:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

次に呼び出すことができます:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}

10
編集のために+1。今日この投稿を読んで、私は1.8を使用しています。どうも。
Gigi2m02 2012

2
編集ありがとうございます。これはボタン専用<a>ですか、それとも使用できますか?私が試してみるとjQuery._data()、次のエラーが表示されますTypeError: a is undefined
Houman

var data = jQuery._data(this[0], 'events')[type];をtry catchで囲み、catchでfalseを返します。イベントがこの[0]にバインドされていない場合、[type]を呼び出すと、「未定義またはnull参照のプロパティ「クリック」を取得できません」のバリエーションが発生します。これにより、知っておくべきことがわかります。
Robb Vandaveer 2014年

_dataオブジェクトがjQuery 2.0.3で変更されたかどうかはわかりませんが、$。inArrayを使用できませんでした。比較する関数は、「ハンドラー」と呼ばれる各データ項目のプロパティにあります。単純なforステートメントを使用するように変更し、文字列の等価性をチェックしました。for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer 2014年

更新/編集を一番上に移動しないのはなぜですか?...これが実際の正解です。
Don Cheadle

179

もう1つの方法-このようなボタンをCSSクラスとフィルターでマークします。

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

最近のjQueryバージョンではbindon次のように置き換えます:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);

4
優れた!コンラッドのおかげで、これはスイートスポットに到達しました。私はこのようなエレガントでシンプルなアプローチが大好きです。すべての要素(既にクリックイベントハンドラーがアタッチされている)は、それぞれを比較するいくつかの大きなイベントバケットでフル検索を実行する必要がないため、パフォーマンスも向上すると確信しています。私の実装では、追加したヘルパークラスに「クリックバインド」という名前を付けました。これは、より保守しやすいオプションだと思います。$( "。list-item:not(.click-bound)")。addClass( "click-bound") ;
Nicholas Petersen

1
受け入れられた回答で述べられているように、「もちろん、このコードが一度だけ呼び出されるようにアプリケーションを構成できれば、最善でしょう。」これはそれを達成します。
Jeff

私の状況では+1、オフ/オン、バインド/アンバインドは複数の呼び出しを防ぎました。これが機能したソリューションでした。
ジェイ

2
コードはCSSクラスの存在と既存のスリップバインディングを確認できるため、これはパフォーマンスの優れたソリューションです。他の解決策は、(少なくとも私が言えることはfr4om)より多くのオーバーヘッドを使用して、バインド解除してから再バインドするプロセスを実行することです。
Phil Nicholas

1
2号。(複数の要素にバインドできるプラグインで使用する場合)ウィンドウオブジェクトにサイズ変更イベントをバインドするときは機能しません。addClass( 'bound')の代わりにdata( 'bound'、1)を使用することもできます。イベントを破棄するとき、他のインスタンスがそれに依存している間にそれを行う場合があります。したがって、他のインスタンスがまだ使用されているかどうかを確認することをお勧めします。
ハーバートピーターズ

59

jQuery 1.7以降を使用している場合:

off前に呼び出すことができますon

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

そうでない場合:

最初のイベントをアンバインドするだけです:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler

1
これは、必ずしもすばらしい解決策ではありませんが、1つのハンドラーのバインドを解除するだけで改善されます.unbind('click', onButtonClicked)マニュアルを参照してください
lonesomeday

16
ハンドラーを追加するときに、実際にハンドラーの名前を付けることができ、バインド解除は非常に簡単になります。$(sel).unbind("click.myNamespace");
Marc

@lonesomeday「unbind」を使用した例は、何か違うものを示すことを意図していたのですか?.unbindは.offと実質的に同じではないので、書く必要があります$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim

1
@ジム私のコメントの3年後に質問が編集されたことがわかります!(また、私のコメントの直後の数分。コメントしていたコンテンツが存在しないため、おそらくあまり意味がなさそうです。)
lonesomeday

@lonesomeday hmmmなぜ編集されたのかわかりませんが、ロールバックする必要があると思いますか?
Naftali別名Neal 2015

8

私が見る最良の方法は、live()またはdelegate()を使用して、各子要素ではなく親でイベントをキャプチャすることです。

ボタンが#parent要素内にある場合は、次のものを置き換えることができます。

$('#myButton').bind('click', onButtonClicked);

沿って

$('#parent').delegate('#myButton', 'click', onButtonClicked);

このコードが実行されたときに#myButtonがまだ存在しない場合でも。


2
廃止されたメソッド
dobs 2017年

8

私はそれを行う「once」と呼ばれる非常に小さなプラグインを書きました。要素内でオフとオンを実行します。

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

そして単に:

$(element).once('click', function(){
});

9
.one()は、最初の実行後にハンドラーをドロップします。
ロドルフォホルヘネメールノゲイラ、2015

3
ここで「1回」は、ハンドラーを1回アタッチするためのものです。jqueryの「1つ」は、一度アタッチするのではなく、一度実行するためのものです。
Shishir Arora 2016

1
私は事実の後で邪魔をしていることを知っていoffますが、指定されたタイプのすべてのハンドラーを削除しませんか?無関係であるが同じアイテムに別のクリックハンドラーがあった場合、そのハンドラーも削除されませんか?
Xandor

イベントで名前空間を使用するだけで、 'keypup.test123'のようなすべてのハンドラーがドロップされません
SemanticZen

6

なぜこれを使わないのですか

unbind()bind()

$('#myButton').unbind().bind('click',  onButtonClicked);

1
$('#myButton').off().on('click', onButtonClicked);私にも使えます。
Veerakran Sereerungruangkul

私のために働く... gr8ソリューション
imdadhusen

綺麗な。既にバインドされているボタンについては、私にとっては完全に機能します。
seanbreeden、

3

これが私のバージョンです:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

使用法:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)

2

@コンラッド・garusの回答に基づいて、しかし、使用してdata、私は信じているので、classスタイリングのために主に使用されるべきです。

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}

2

チェック/バインド/バインド解除を回避するために、アプローチを変更できます!Jquery .on()を使用しないのはなぜですか?

Jquery 1.7以降、.live()、. delegate()は非推奨となったため、.on()を使用して

現在および将来の現在のセレクターに一致するすべての要素のイベントハンドラーをアタッチします。

つまり、まだ存在する親要素にイベントをアタッチし、存在するかどうかに関係なく子要素をアタッチできるということです。

次のように.on()を使用すると:

$('#Parent').on('click', '#myButton'  onButtonClicked);

親のイベントクリックをキャッチし、存在する場合は子「#myButton」を検索します...

したがって、子要素を削除または追加するときに、イベントバインディングを追加または削除するかどうかを心配する必要はありません。


これは、現在の標準に対するここでの最善の答えです。親にハンドラーを設定して子を監視するこの機能はよく宣伝されているとは思いませんが、かなりエレガントなソリューションです。イベントが複数回発生し、そのたびにイベントの合計回数が増加し続けました。この1行ですべてのトリックが実行されましたが、使用せず.offにプロセス内の無関係なハンドラーを削除すると思います。
Xandor


0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}

0

2019年6月の時点で、機能を更新しました(必要に応じて機能します)

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};

-2

jQueryにはソリューションがあります

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

同等:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});

1
あなたの答えは質問とは関係ありません。問題は、関数を要素のイベントに1回だけバインドすることです。
のMichałザレウスキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.