うっかりミスでクリックしてほしくないボタンの制御について～プロアクティブなガード方式～

本記事はQmonus Value Streamの投稿キャンペーン記事です。

そのボタンを間違ってクリックされてしまうと後々が面倒なことになる場合、「本当に○○しますか？」とダイアログを表示して、「OK」ボタンをクリックしたら先に進むような実装をよく見ます。

この、ボタンをクリックしてからダイアログを表示する方式はリアクティブすぎないか？と疑問に思いました。本当に○○してほしくなかったら、プロアクティブにガードしたほうが確実なのでは？と。

下図は、削除ボタンを例に、プロアクティブな物理的なボタンとリアクティブなWebUIのボタンを比較したものです。あと物理ボタンをヒントにプロアクティブなWebUIボタンも考えてみました。

物理ボタンのイメージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76786/c263c0a0-bc27-7ab7-d247-379cf98982af.png)

WebUIのボタンイメージ:
https://codepen.io/madilloar/pen/eYaqRqG

<p class="codepen" data-height="300" data-theme-id="light" data-slug-hash="eYaqRqG" data-default-tab="js,result" data-user="madilloar" data-embed-version="2" data-pen-title="ペンタイトル"></p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


# 削除ボタンを例に、プロアクティブな物理的なボタンとリアクティブなWebUIのボタンを比較
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76786/c483021e-56ed-b3ac-e328-54a913dd9c90.png)

# プロアクティブなWebUIボタンの実装例

```html: プロアクティブなWebUIボタンの実装例
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>カスタムボタン</title>
    <style>
        #deleteButton1:disabled {
            background-color: grey;
            cursor: not-allowed;
        }
        #deleteButton2:disabled {
            background-color: grey;
            cursor: not-allowed;
        }
    </style>
</head>
<body>
    削除ボタンとalertの組み合わせ→<button id="deleteButton0">削除</button>
    </p>
    削除するならチェックボックスをONにしてください→<input type="checkbox" id="confirmCheckbox">
    <button id="deleteButton1" disabled>削除</button>
    <p/>
    スライダーを右端までスライドすると削除ボタンが活性化します→
    <input type="range" id="slider" min="0" max="100" value="0">
    <button id="deleteButton2" disabled>削除</button>
    </p>

    <script>
        // 削除ボタンとalertの組み合わせ
        const deleteButton0 = document.getElementById('deleteButton0');

        deleteButton0.addEventListener('click', function() {
            // 確認ダイアログを表示
            if (confirm('本当に削除しますか？')) {
                performDeletion();
            } else {
                console.log('削除がキャンセルされました');
            }
        });

        // 削除処理を行う関数（例）
        function performDeletion() {
                // ここに削除処理を追加
            alert('削除されました');
        }
        
        
        // チェックボックス+削除ボタン方式
        const checkbox = document.getElementById('confirmCheckbox');
        const deleteButton1 = document.getElementById('deleteButton1');

        checkbox.addEventListener('change', function() {
            deleteButton1.disabled = !this.checked;
        });

        deleteButton1.addEventListener('click', function() {
            if (!deleteButton1.disabled) {
                // ここに削除処理を追加
                alert('削除されました');
                // ボタンを再度非活性化し、チェックボックスをリセット
                checkbox.checked = false;
                deleteButton1.disabled = true;
            }
        });
        
        
        // スライダー+削除ボタン方式
        const slider = document.getElementById('slider');
        const deleteButton2 = document.getElementById('deleteButton2');

        slider.addEventListener('input', function() {
            // スライダーが最大値に達したらボタンを活性化
            if (this.value == this.max) {
                deleteButton2.disabled = false;
            } else {
                deleteButton2.disabled = true;
            }
        });

        deleteButton2.addEventListener('click', function() {
            // ここに削除処理を追加
            alert('削除されました');
            // ボタンを再度非活性化し、スライダーをリセット
            deleteButton2.disabled = true;
            slider.value = slider.min;
        });
    </script>
</body>
</html>
```