幾天前我展示了兩款免 JavaScript 的刪除確認設計,讀者 Dante Lee 主張勾 Checkbox 並不算好設計,它一樣需要額外移動滑鼠加點選,還會讓人想起被迫違背自由意志勾選「我同意」的不愉快回憶 (我他X的根本不想同意啊)。

想想,這說法確實有幾分道理,於是我厚著臉皮交流,索取到一個好點子:何不要求使用者長按一段時間以確認意圖?這樣即可防止誤觸,又不需要額外滑鼠動作,耐心等待即可。

週末有點時間,就用這個題目暖身試做了 PoC,順便加碼我又想到的另一個點子:刪除時使用者必須加按 Shift 鍵,也是不需要額外滑鼠操又有防誤觸效果。

展示如下,第一種做法是按住超過 3 秒才會真的執行刪除:

第二做法是刪除按平時用 disabled 鎖定防止誤按,要按 Shift 鍵才會解鎖。

Talk is cheap, here is the code.

原理不難,所以這回我沒用 Vue.js,直接用香草 JavaScript 結局這回合,完整範例如下:

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>更多確認刪除玩法</title>
    <style>
        html,
        body {
            font-family: 'Courier New', Courier, monospace;
        }
        fieldset {
            border: 1px solid #ccc;
            padding: 10px;
            margin: 10px;
            width: 300px;
            label {
                cursor: pointer;
            }
        }
    </style>
</head>

<body>

    <fieldset>
        <legend>Click and Hold</legend>
        <form method="post" action="/delete">
            <span>長按 3 秒進行刪除</span>
            <button type="submit" data-mode="long-press">
                刪除<span></span>
            </button>
        </form>
    </fieldset>
    <style>
        [data-mode="long-press"] {
            width: 75px;
            span {
                display: none;
            }
            &.delay {
                background-color: pink;
                color: #333;
                cursor: progress;
                span {
                    display: inline;
                }
            }
        }
    </style>
    <script>
        document.querySelectorAll('button[data-mode="long-press"]').forEach(button => {
            let timer;
            let countDown;
            function updateButtonText() {
                button.querySelector('span').textContent = countDown > 0 ? `(${countDown})` : '';
            }   
            button.addEventListener('mousedown', () => {
                countDown = 3;
                button.classList.add('delay');
                updateButtonText();
                timer = setInterval(() => {
                    countDown--;
                    updateButtonText();
                    if (countDown === 0) {
                        reset();
                        button.form.submit();
                    }
                }, 1000);
            });
            function reset() {
                clearTimeout(timer);
                button.classList.remove('delay');
            }
            button.addEventListener('mouseup', reset);
            button.addEventListener('mouseleave', reset);
            button.addEventListener('click', (e) => {
                e.preventDefault();
                reset();
            });
        });
    </script>

    <fieldset class="shift-required">
        <legend>Shift Required</legend>
        <form method="post" action="/delete">
            <span>請按 Shift 確認要刪除</span>
            <button type="button" class="dummy" disabled>刪除</button>
            <button type="submit" class="actual">刪除</button>
        </form>
    </fieldset>
    <style>
        .shift-required {
            .actual {
                display: none;
            }
        }
        .shift-required.shift {
            .actual {
                display: inline-block;
            }
            .dummy {
                display: none;
            }
        }        
    </style>
    <script>
        window.addEventListener('focus', function () {
            document.querySelector('.shift')?.classList.remove('shift');
        });
        document.addEventListener('keydown', function (e) {
            if (e.key === 'Shift') {
                document.querySelector('.shift-required').classList.add('shift');
            }
        });
        document.addEventListener('keyup', function (e) {
            if (e.key === 'Shift') {
                document.querySelector('.shift-required').classList.remove('shift');
            }
        });
    </script>

</body>

</html>

This article explored two delete confirmation designs: a long-press button and a shift-key requirement. These alternatives aim to improve user experience by reducing accidental actions without extra mouse movements.


Comments

Be the first to post a comment

Post a comment