3

私のページには、ビューを表すマークアップのチャンクと、そのビューに関連付けられている JS コントローラー関数があります。(これらは Angular ですが、それは問題ではないと思います。) コントローラー コードは、アプリの別の場所から発生したカスタム イベントをリッスンし、コントローラー固有のロジックでそのイベントを処理します。

私の問題は、コントローラーのイベント ハンドラーが何度もアタッチされていることです。ビューが再アクティブ化されるたびにアタッチされ、カスタム イベントが発生するたびにハンドラーが複数回実行されます。ハンドラーをイベントごとに 1 回だけ実行したい。

.off()ハンドラーをバインドする前にバインドを解除するために使用しようとしました。.one()ハンドラーが 1 回だけ実行されるようにしました。here$.proxy()との相互作用について読んだ後、試してみました。.off()

ここに私のコードのスケッチがあります:

// the code inside this controller is re-run every time its associated view is activated
function MyViewController() { 

    /* SNIP (lots of other controller code) */

    function myCustomEventHandler() {
        console.log('myCustomEventHandler has run');
        // the code inside this handler requires the controller's scope
    }

    // Three variants of the same misbehaving event attachment logic follow: 

    // first attempt
    $('body').off('myCustomEvent', myCustomEventHandler);
    $('body').on('myCustomEvent', myCustomEventHandler);
    // second attempt
    $('body').one('myCustomEvent', myCustomEventHandler);
    // third attempt
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this));
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this));
    // all of these result in too many event attachments

};

// ...meanwhile, elsewhere in the app, this function is run after a certain user action
function MyEventSender() {
    $('body').trigger('myCustomEvent');
    console.log('myCustomEvent has been triggered');
};

アプリ内をクリックして面倒なビューに 5 回切り替えてから、実行されるアクションを実行するMyEventSenderと、コンソールは次のようになります。

myCustomEvent has been triggered
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run

次のようにするにはどうすればよいですか。

myCustomEvent has been triggered
myCustomEventHandler has run

???

4

3 に答える 3

3

イベントに名前空間を与えてから、コントローラーを再実行するときに、その名前空間を持つすべてのイベントを単純に削除します。

jsbin

$('body').off('.controller');
$('body').on('myCustomEvent.controller', myCustomEventHandler);
于 2013-05-10T17:47:58.367 に答える
2

問題は、function MyViewController(){}が複数回呼び出されると、(現在のクロージャにアタッチされた) の別のインスタンスが取得されるmyCustomEventHandlerため、それを に渡しても$.off前のハンドラが登録解除されないことです。

KevinB の答えであるイベント名前空間は、どのハンドラーがインストールされているかを知らなくても、特定のハンドラーを削除するために私が提案するものです。要素が削除/非表示になったときにイベントの登録を解除できれば、他のコードが同じイベント名前空間に追加した可能性のあるハンドラーを削除する危険を冒さずに、登録解除する関数への参照を取得できます。結局のところ、イベントの名前空間は単なる文字列のグローバル プールであり、名前の衝突の影響を受けやすくなっています。

関数を globalにすると、それも機能します (クロージャーが必要なように見えることを除いて) が、問題を説明するためにそれを示しているだけです。名前空間を使用してください

function myCustomEventHandler() {
    console.log('myCustomEventHandler has run');
    // the code inside this handler requires the controller's scope
}

function MyViewController() { 

    // first attempt
    $('body').off('myCustomEvent', myCustomEventHandler);
    $('body').on('myCustomEvent', myCustomEventHandler);
    // second attempt
    $('body').one('myCustomEvent', myCustomEventHandler);
    // third attempt
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this));
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this));

}

// ...meanwhile, elsewhere in the app, this function is run after a certain user action
function MyEventSender() {
    $('body').trigger('myCustomEvent');
    console.log('myCustomEvent has been triggered');
}
MyViewController();
MyViewController();
MyEventSender();

前のアイデア

問題の 1 つは、同じ関数を$.onand$.offに渡していないため、この場合、 off は何も登録解除していないことです。

問題ではありません。正確には直感的ではないため、参考までに答えを残してください。$.proxy同じ関数とコンテキストが渡された場合、同じバインドされた関数への参照を返すようです。http://jsbin.com/adecul/9/edit

于 2013-05-10T17:35:19.427 に答える