トピックを検索
押した瞬間に実行され、キャンセルできないボタン
ボタンがpointerdownで処理を実行するため、誤って押したときもすぐに実行され、指を離す前にずらしても止められません。
addEventListener('pointerdown', addToBasket) 問題
押すボタンを間違えた人が、指を離す前にずらしても取り消せません。
ほとんどのボタンは、触れたときではなく、指を離したときに決まります。その間に、押し間違えた人は指をずらして考え直せます。多くの人が、意識せずにそうしています。あるショップは、商品をカードで並べ、それぞれに「カートに入れる」ボタンを置いています。反応を速く感じさせるため、ボタンは離したときではなく、押した瞬間に商品を追加します。押してから別のタオルだと気づき、離す前に指をずらしても、カートの数はもう増えています。スマートフォンではさらに困ります。ボタンの上でスクロールを始めると、ページが動き出すと同時に商品が追加されるため、商品一覧をスクロールするだけで、気づかないうちにカートが埋まっていきます。
WCAG 2.5.2「ポインタのキャンセル」は、ポインターで1回押した操作を取り消せることを求めています。押したとき(ダウンイベント)には何も実行しないか、離したとき(アップイベント)に実行して、離す前に中止する方法や後から元に戻す方法を用意するか、離したときに押したときの結果を打ち消すか、ピアノの鍵盤のように押した瞬間の実行が不可欠である必要があります。この例では、処理全体をダウンイベントの pointerdown で実行しており、不可欠な理由もありません。「元に戻す」ボタンを付けても解決しません。元に戻す方法で満たせるのは、アップイベントで完了する処理だけだからです。W3Cは、このパターンを失敗例F101「ダウンイベントで操作要素を作動させる」として挙げています。
親指で長い商品一覧をスクロールする人は誰でも、同じ問題に出会います。スマートフォン、タブレット、タッチ操作のノートパソコンのどれでも、指が触れた瞬間に押下が発生します。
10グループ中3グループに影響
- 全盲 (影響なし)
- 弱視 (影響あり)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 届く範囲・力 (影響あり)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 届く範囲・力が限られる 手にふるえがある人は、隣のボタンを押したり、別のボタンへ向かう途中で触れたりしがちです。離す前に指をずらすのが取り消す方法ですが、ここでは効かず、商品はもうカートに入っています。
- 手の操作が限られる ヘッドポインター、口にくわえたスタイラス、ゆっくり動かすポインターを使う人は、狙いを定める前に画面に触れてしまうことが多く、最初に触れた時点で押したことになります。
- 視覚が限られる 拡大鏡を使う人は、ポインターの周りしか見えないため、先に押してから、押したまま下に何があるかを確かめることがよくあります。押した瞬間に実行されると、確かめる時間がありません。
ボタンには名前、役割、フォーカス表示があり、マークアップのチェックはすべて合格します。スクリプトのテストは click() を呼ぶか、同じ位置で押して離す操作を一度に行うため、壊れたボタンでも成功します。正しい手順だけを試すテストでは、押し間違いは起きません。フレームワークはリスナーを要素以外に付けることが多く、スキャナーにはスクリプトがどのイベントを聞いているか分かりません。押したときのリスナーがあるだけでは問題とは言えず、波紋のエフェクトを描くために使うボタンも多くあります。AIツールは、反応を速くするためや、入力欄のフォーカスが先に外れるのを防ぐために、onMouseDown や onPointerDown を提案することがよくあります。手が安定していれば、デモでは問題なく動きます。
試してみる
「カートに入れる」を押したままにして、いつ追加されるか見てください。次に、離す前にボタンの外へずらしてください。
ボタンは、触れたときではなく、離したときに決まります。その間があるから、押し間違えた人やスクロールしたかっただけの人も考え直せます。ショップの下の欄で、このボタンが待ってくれるか確かめてください。スマートフォンでは、ボタンに指を置いたまま一覧をスクロールしてみてください。
問題あり 修正できますか?
- リネンのティータオル
- コットンのエプロン
- オークのまな板
- ホーローのマグ
修正方法
処理をclickに移し、押したときは押された見た目を示すだけにします。
- 1処理は押したときではなく、要素の上で離したときに実行する。
- 2離す前に外へずらせば取り消せ、スクロールは押下として扱わない。
- 3押したときのリスナーは、押下の見た目、フォーカスの維持、ドラッグの開始に使ってよいが、処理を完了させない。
<button type="button" class="add">カートに入れる</button><button type="button" class="add">カートに入れる</button>この方法を選ぶ理由:まずHTMLの要素を使う
pointerdown は、マウス、ペン、指がボタンに触れた瞬間に発生します。そのため、何を押したか確かめたり、指をずらしたりする前に商品が追加されます。タッチ操作では、ボタンの上で始めたスクロールのたびにも発生します。mousedown、touchstart、フレームワークの onMouseDown や onTouchStart も同じ理由で問題になります。
2つのリスナーを削除し、ボタン本来の click を使います。ブラウザーは、押すのも離すのもボタンの上だったときにだけ click を発生させます。そのため、離す前に外へずらせば取り消せ、スクロールに変わったタッチがクリックになることもありません。同じイベントが、フォーカスしたボタンでのEnterキーとSpaceキー、スクリーンリーダーのダブルタップ、音声操作でも発生するため、キーボード用のコードも不要になります。離したときに実行しても、遅くは感じません。押してから離すまでの時間は、利用者が判断に使う時間です。
押したときのリスナーは、処理を完了させない限り使ってかまいません。次の使い方はよくあり、問題ありません。
- 押下中の見た目:CSSの
:activeならスクリプトは不要です。pointerdownで波紋を描いても、処理がclickを待つなら問題ありません。 - フォーカスの維持:候補の一覧を
mousedownで選ぶのは、多くの場合、検索欄のフォーカスが先に外れて一覧が閉じるのを防ぐためです。mousedownは残し、event.preventDefault()でフォーカスの移動を止めるだけにして、候補はclickで選びます。一覧のそれ以外の作りは「候補を読み上げず、キーボードで選べないオートコンプリート」で説明しています。 - ドラッグの開始:ドロップは離したときに起こり、どこにも置けない場所で離したら、項目を元の位置に戻します。ドラッグ以外の移動方法は「マウスでしか動かせないドラッグ&ドロップのボード」で説明しています。
- 長押し:押している間だけプレビューを表示し、離すと消えるものは問題ありません。離したときに押した結果を打ち消しているからです。「長押しで削除」のように何かを確定させる長押しは、離したときに完了させ、ポインターが外れたら取り消します。
修正に見えても、修正にならないものもあります。pointerup、mouseup、touchend はアップイベントですが、取り消しができません。Chromiumでは、指でページをスクロールした後でも、ボタンで touchend が発生します。マウスの場合、別の場所で押してからボタンの上へ移動して離すと、そのボタンで pointerup が発生します。普通のボタンで setPointerCapture() を使うと、離したイベントがボタンに戻され、遠く離れた場所で離しても、ブラウザーはボタンで click を発生させます。キャプチャーは、本当にドラッグするものだけに使ってください。
コンポーネントライブラリでは、これを押下イベントとしてまとめていることがよくあります。「押し始め」のイベントではなく、onPress のように要素の上で離したときに発生するものを使い、名前を信じずに、外へずらすテストで確かめてください。リンクは対応不要です。リンクをたどる動作はもともとクリックです。削除や支払いのように影響が残る処理では、離したときに実行するだけでは守りとして弱いため、元に戻す方法や確認も加えてください。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得
修正の確認
5つの確認
確認方法: マウス、タッチ、キーボード、スクリーンリーダー
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通のボタンやカードのコンポーネントを変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。画面上のキーボードのキーや楽器のように、押した瞬間に動くこと自体が目的の操作もあり、WCAGはそれを不可欠なものとして扱います。小さなボタンは、そもそも押し損ねることもあります。大きさについては「ターゲットサイズが足りない、詰まったボタン」で説明しています。マウスだけでなく、実際のタッチ端末でも操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。