発生した問題
以下の手順で操作すると、Webページのボタンが無反応になる。(クリックしても動かない)
- Webページでボタンをクリックして画面遷移
- 画面遷移後、ブラウザバックして前の画面に戻る
- ボタンをクリックしても反応しない
上記の問題が「Firefox」「IE」「iOSのSafari」などで発生した。
開発に利用しているChromeでは起きないので気がつかないよぉ
原因
ボタンが無反応?
反応しないボタンを調査すると"disable"という属性が……
どうやら、ボタンの二重クリック対策のjavascriptが動作して、ボタンをクリックできないようにしていたよう。
原因はわかったけれど、どうしてこんな属性が?(ブラウザバックだけど)画面を表示しただけだよ
ボタンがdisableになっている?
どうやら同じ問題に直面している人は多いようで、以外とさくっと原因が判明した。
「Back Forward Cache(BFcache)」という親切(?)機能が原因らしい。
BFcacheの詳細についてはGoogleで検索。
Firefoxではデフォルトでこの機能がONになっているらしいですよ。
それで、以下の流れでこの問題が発生しているよう。
画面A表示(状態1)
画面Aでボタンをクリック
↓
画面Aでスクリプトが動き、ボタンをdiableにする(状態2)
↓
画面Bに遷移
ブラウザバックする
↓
BFcacheにより画面遷移直前の状態、つまり「状態2」の画面Aが表示される
さらに、loadイベントが発生「しない」ので、loadでひも付けたスクリプトが動作しない
対策
対策1(失敗)
「onloadが動かないならば、動かすようにすればいいじゃない」ということで、以下の処理をスクリプトに追加。
$(document).ready(function(){
//~ボタンのdisble解除~
//~ボタン二重対策処理~
});
window.onunload = function(){}
※jQuery使ってます
unloadイベントに空の処理を追加することで、ブラウザバック時もloadイベントが動作するというもの
詳細な理由は調べたけど忘れた。
対策後にFirefox、IE11で確認したら、無事に動いている。よかったよかった
iOS mobile Safari「無駄無駄無駄無駄無駄無駄無駄無駄っ!」
iOSのSafariさんが言うことを聞いてくれませんでした。
対策2(ほぼ成功)
iOSのSafariでは"load"、"unload"イベントが非推奨になっているようです。
その代わりに"pageshow"、"pagehide"イベントを使うらしい。
ということで、スクリプトを最初修正
window.onpageshow = function(){
//~ボタンのdisble解除~
//~ボタン二重対策処理~
}
これでiOS Safariでも動きました。解決解決
対策3(これで完璧)
満足して他の画面を触っていると、どうも動かないボタンがある。
asp:repeaterコントロールで繰り返し作成されるボタンがクリックしてもウンともスンとも言ってくれない。
それで、このボタンを調べたのだが、どうやらこいつはASP.net側がjavascriptの処理をバインドさせている様子(たぶん。自信ない)
おそらく、クリック時にdisable処理が入ったせいで動いていないようだ。
ということで、disabled属性をつけるタイミングをずらすことで解決した。
最終的な処理はこちら
window.onpageshow = function(){
var $btn = $("input[type='submit']");
//~ボタンのdisbled解除~
$btn.prop("disabled", false);
//~ボタン二重対策処理~
$btn.on("click", function(){
// ボタンクリックに関する一連の処理後にクリック無効化処理を実行させる
setTimeout(function() {
$btn.prop("disabled", true);
}, 0);
});
}
ふう、解決解決。
課題
解決解決、と書いたけれど、実はまだ問題が残っている。
"pageshow"イベントが古いIEだと動作しないらしい。(IE10か11から対応)
今回は古いIEの面倒を見なくてもOKということで確認していないが、多分これだと動かない。
なので、古いIE(~IE10)でも たぶん 動作するようにしたものがこちら。
function notDoubleClick(){
var $btn = $("input[type='submit']");
//~ボタンのdisble解除~
$btn.prop("disabled", false);
//~ボタン二重対策処理~
$btn.on("click", function(){
// ボタンクリックに関する一連の処理後にクリック無効化処理を実行させる
setTimeout(function() {
$btn.prop("disabled", true);
}, 0);
});
}
if (typeof window.onpageshow != "undefined") {
window.onpageshow = function() {
notDoubleClick();
}
} else {
window.onload = function() {
notDoubleClick();
}
window.onunload = function () { }
}
たぶん、なので動作しないかも。