はじめに
- aタグのリンクを押したときにformのsubmitを実行する、というHTMLがあった
- マークアップが先に作られておりこれを担保しつつ、ボタン機能を持たせる必要があった
- 例として以下のようなマークアップで「ログイン」ボタンが作られていたとする
<p>
<a href="{{ route('lognin') }}" onclick="event.preventDefault(); document.getElementById('login').submit();">
ログイン
</a>
</p>
<form id="login" action="{{ route('login') }}" method="POST">
@csrf
</form>
処理としては
1, aタグで作られたログインボタン押す
2, login
の要素を持つformタグ内でsubmit()を実行
3, actionで指定したルートへPOSTする
といった感じで処理が走る状態
やりたいこと
今回はこのaタグがダブルクリックされないようにして、余計なリクエストがサーバーに投げられるのを防ぎたかった
環境
- (Laravel 8.0)
- jQuery 2.2.4
やったこと
いくつか記事を参考させていただきました。
1, 2回目クリック以降はdisable属性を付与
- aタグには対応しておらず、断念
- inputタグ、buttonタグなどにはdisabledが対応
https://www.nishishi.com/javascript-tips/button-disabled.html#button-disabled0-source
2, 2回目クリック以降は何も処理をしない
- aタグのonClick→直接JSに入っているので、formのsubmitをする、という処理ができず断念
https://stackoverflow.com/questions/15622100/how-can-i-disable-href-if-onclick-is-executed
3, 2回目クリック以降はalertを出す
- 2回目のクリックはPOST送信されない対応はできた
- ただ、他の画面と比べてフロント制御方法としてイレギュラー対応になるため却下
結果
機能しない、もしくは実装方法が気に入らないという理由で自分で作ることにしました。。。
対策
調べた記事にあった要素をかき集めて以下の流れで対応としました
1, クリック時にJS(jQuery)のイベント発火
2, イベント内でaタグのリンク機能を無効化する
3, ブラウザバック対応としてリンク機能を再度有効化
参考:
https://freeladay.com/?p=2273#CSSadisabled
-
pointer-events
というstyleを初めて知った
https://developer.mozilla.org/ja/docs/Web/API/Element/setAttribute
https://developer.mozilla.org/ja/docs/Web/API/Element/removeAttribute
- JSで属性を付与したり、削除したりできるのかと知った
これらを使ってコードに落とし込んだのが以下です。
...
<-- jQueryを発火させるclassを追加 -->
<p class="disabled">
<a href="{{ route('lognin') }}" onclick="event.preventDefault(); document.getElementById('login').submit();">
ログイン
</a>
</p>
<form id="login" action="{{ route('login') }}" method="POST">
@csrf
</form>
...
<-- JS読み込み(読み込み方はお任せします) -->
<script src="{{ mix('js/app.js') }}"></script>
...
$(document).on('click','.disabled',function (event) {
// イベント発火箇所から一番近いaタグを取得
const target = event.target;
const $a = $(target).closest('a');
// aタグの有無確認
const hasStyle = $a[0].hasAttribute('style');
let currentStyle = null;
// aタグ無効化
if (hasStyle) {
currentStyle = $a[0].getAttribute('style');
$a[0].setAttribute('style', currentStyle + '; pointer-events: none;');
} else {
$a[0].setAttribute('style', 'pointer-events: none;');
}
// 時間経過(5s後)でaタグ無効化を解除
setTimeout(function () {
if (hasStyle) {
$a[0].setAttribute('style', currentStyle);
} else {
$a[0].setAttribute('style', 'cursor: pointer;');
}
}, 5000);
});
ポイントはブラウザバック対応のためにsetTimeoutでstyleを初期化している点ですね。
※ちなみに$a[0]
の0
が必要であるのはeventに複数要素が入っていることから必要になります。
console.logでevent.target
を出力してみるとわかると思います。
おわりに
- なんともダブルクリック対応やるにしては冗長感が否めないのですが、今のところこの形で収まりました。
- 特に現状styleの有無をチェックする必要がある点で冗長になっている感覚。
- プロジェクトの都合でこの形をとっているので、参考になるかはわかりませんが一つの実装方法の選択肢、程度に思ってもらえればいいかと思います。
- 他にも「何かこんな形でできるのでは?」とかあれば気軽に教えていただけると幸いです。
最後までお読みいただきありがとうございました。