0
1

More than 1 year has passed since last update.

[JavaScript]aタグのダブルクリック対策をしたい

Last updated at Posted at 2022-01-22

はじめに

  • 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送信されない対応はできた
    • ただ、他の画面と比べてフロント制御方法としてイレギュラー対応になるため却下

https://qiita.com/tabo_purify/items/7d47656bd7c6332efe8a#%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%AE%E9%80%A3%E7%B6%9A%E3%82%AF…

結果

機能しない、もしくは実装方法が気に入らないという理由で自分で作ることにしました。。。

対策

調べた記事にあった要素をかき集めて以下の流れで対応としました

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で属性を付与したり、削除したりできるのかと知った

これらを使ってコードに落とし込んだのが以下です。

index.html
...
<-- 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>
...
app.js
$(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の有無をチェックする必要がある点で冗長になっている感覚。
  • プロジェクトの都合でこの形をとっているので、参考になるかはわかりませんが一つの実装方法の選択肢、程度に思ってもらえればいいかと思います。
  • 他にも「何かこんな形でできるのでは?」とかあれば気軽に教えていただけると幸いです。

最後までお読みいただきありがとうございました。

0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1