ユーザーが特定のリンクをクリックすると、確認ダイアログが表示されます。 [はい]をクリックした場合、元のナビゲーションを続けたいと思います。ワンキャッチ:確認ダイアログは、ユーザーが[はい]ボタンをクリックした場合にのみ解決されるjQuery.Deferredオブジェクトを返すことで実装されます。したがって、基本的に確認ダイアログは非同期です。
だから基本的に私はこのようなものが欲しい:
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
})
})
もちろん、フラグを設定してクリックを再トリガーすることもできますが、それは面倒です。これを行う自然な方法はありますか?
以下は、Chrome 13で実際に動作したコードの一部です。驚いたことに。
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
これは非常にクロスブラウザではなく、将来的に修正されるかもしれません。
そして、イベントの伝播をキャンセルするとどうなるかわかりません。
問題を正しく理解している場合、イベントを更新して、そのクロージャーの元のイベントにすることができると思います。したがって、.done関数でe = e.originalEventを設定するだけです。
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
これは別の例のフィドルです(メッセージを表示できるようにコンソールを開いたままにしてください):これはchrome and firefoxで機能しました
私は自分のプロジェクトの1つでこの方法で問題を解決しました。この例は、クリックなどの基本的なイベント処理で動作します。確認用のハンドラーは、最初にハンドラーにバインドする必要があります。
// This example assumes clickFunction is first event handled.
//
// you have to preserve called function handler to ignore it
// when you continue calling.
//
// store it in object to preserve function reference
var ignoredHandler = {
fn: false
};
// function which will continues processing
var go = function(e, el){
// process href
var href = $(el).attr('href');
if (href) {
window.location = href;
}
// process events
var events = $(el).data('events');
for (prop in events) {
if (events.hasOwnProperty(prop)) {
var event = events[prop];
$.each(event, function(idx, handler){
// do not run for clickFunction
if (ignoredHandler.fn != handler.handler) {
handler.handler.call(el, e);
}
});
}
}
}
// click handler
var clickFunction = function(e){
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(go.apply(this, e));
};
// preserve ignored handler
ignoredHandler.fn = clickFunction;
$('.confirmable').click(clickFunction);
// a little bit longer but it works :)
私たちのプロジェクトには同様の要件があり、これは私にとってはうまくいきます。 chromeおよびIE11でテスト済み。
$('a.my-link').click(function(e) {
e.preventDefault();
if (do_something === true) {
e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
do_something = false;
// this allows user to navigate
$(e.target).click();
})
}
})
コードを編集しました。追加した新機能:
コード:
$('a.my-link').on("click.myEvent", function(e) {
var $that = $(this);
$that.off("click.myEvent");
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$that.trigger("click");
});
});
私はこれを解決しました:
function async() {
var dfd = $.Deferred();
// simulate async
setTimeout(function () {
if (confirm('Stackoverflow FTW')) {
dfd.resolve();
} else {
dfd.reject();
}
}, 0);
return dfd.promise();
};
$('.container').on('click', '.another-page', function (e) {
e.stopPropagation();
e.preventDefault();
async().done(function () {
$(e.currentTarget).removeClass('another-page').click();
});
});
$('body').on('click', function (e) {
alert('navigating somewhere else woot!')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<a href="#" class="another-page">Somewhere else</a>
</div>
リンク自体ではなく、親にイベントリスナーを追加した理由は、jQueryのon
イベントが別の方法で通知されるまで要素にバインドするためです。そのため、エレメントにクラスanother-page
がなくても、イベントリスナーがアタッチされているため、event delegation
を利用してこの問題を解決する必要があります。
[〜#〜] gotchas [〜#〜]これは非常に状態ベースです。つまり、ユーザーがリンクをクリックするたびにユーザーに確認する必要がある場合は、2番目のリスナーを追加してanother-page
クラスを再度リンクに追加する必要があります。すなわち:
$('body').on('click', function (e) {
$(e.currentTarget).addClass('another-page');
});
side noteユーザーが同意した場合はcontainer
のイベントリスナーを削除することもできます。これを行う場合は、必ずnamespace
イベントを使用してください。誤って削除する可能性のあるコンテナの他のリスナーである可能性があります。詳細については、 https://api.jquery.com/event.namespace/ を参照してください。