# 全角数字を受け付けず、理由も示さない入力欄

Source: https://easeweb.dev/learn/full-width-digits
Topics: フォーム > 入力欄が全角と半角のどちらも受け付ける (WCAG 3.3.1, 3.3.3)
The fix: 番号を確認する前に変換し、全角の数字やハイフンを半角にして、同じ番号として送信します。どうしても半角の数字しか受け付けられない欄では、番号を「正しくない」と言うだけのメッセージをやめ、半角で入力するようメッセージで伝えます。入力中の文字を欄から削除することは、どちらの場合もしません。
Test with: input method, keyboard, screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html https://www.w3.org/WAI/WCAG22/Understanding/error-suggestion.html https://www.w3.org/WAI/WCAG22/Techniques/general/G85 https://www.w3.org/WAI/WCAG22/Techniques/general/G177 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize https://unicode.org/reports/tr15/ https://design-system.service.gov.uk/patterns/telephone-numbers/

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

## 場面

ショップの購入手続きで、配送業者からの連絡用に電話番号を入力します。直前まで住所を入力していたので、日本語入力はオンのままです。ひらがなモードでは、IMEの設定によっては、数字が全角で入力されます。0123456789ではなく、０１２３４５６７８９です。読む人にとっては同じ数字で、かなと同じ幅で少し広く表示されるだけです。日本語の文書からコピーした番号が全角のこともあります。

この欄のスクリプトは半角の0〜9しか想定しておらず、よくある書き方は次の2つです。

- フォームを送信するときに番号を確認し、「正しい電話番号を入力してください。」とはじく。利用者は番号を見返し、数字が合っていることを確かめて送り直しますが、同じメッセージが出ます。番号は合っていて、幅だけが違うことは、どこにも書かれていません。
- 欄を数字だけに保つため、`input` のハンドラーでそれ以外の文字を削除する。このハンドラーにとって全角数字は0〜9ではないため、入力したそばから消えていき、メッセージも出ません。ひらがなモードで打ったハイフンは長音符の「ー」になり、同じように消えます。

## 確認できる問題

数字そのものに誤りはありません。欄が、どの書き方で入力するかを伝えないまま値をはじいているので、問題は、そのときページが何を伝えるかにあります。

達成基準3.3.3「エラー修正の提案」は、入力エラーを検出し、修正方法が分かっている場合は、それを提案することを求めています。ページが求めているのは半角の数字だと、はっきりしています。確認で調べているのは、まさにそこだからです。それなのに「正しい電話番号を入力してください」は、そのことを伝えていません。この達成基準の解説書は、誰のための基準かも挙げています。認知に障害があり、エラーの直し方を理解しにくい人と、視覚に障害があり、直し方を正確に把握できないことがある人です。幅の違いはほとんど見分けがつかないため、どちらの人も、直し方を推測できません。

達成基準3.3.1「エラーの特定」は、ページが検出した入力エラーについて、どの項目かを特定し、テキストで説明することを求めています。1つ目の修正前は、これを満たします。メッセージは欄を示し、値が正しくないと伝えており、解説書も「Email is not valid」は3.3.1を満たすとしています。2つ目は満たしません。ハンドラーは受け付けない文字を検出して削除しますが、削除したことを伝えるテキストはどこにもありません。解説書は、ページが自動で値を変える場合にも触れており、そのときもエラーは利用者に説明しなければならないとしています。

## 利用者への影響

日本語で入力する人なら、能力にかかわらず、この問題に出会うことがあります。数字が全角で入力されるのはIMEの設定の1つで、利用者の誤りではありません。下に挙げるのは、特に負担が大きい人です。

## 自動チェックとAIの限界

欄にはラベルがあり、メッセージも欄に関連付けられているため、マークアップに誤りはなく、スキャンも通ります。スキャンは文字を入力せず、日本語入力を使わずに確認する人は半角の数字しか入力しないため、番号の確認はいつも通ってしまいます。AIアシスタントに「数字だけを受け付ける電話番号欄」を頼むと、下の修正前のどちらかのコードが返ってくることがあります。EaseWebの診断は、数字を入力する欄に全角の番号を設定して `input` イベントを送ります。ハンドラーが番号を削除し、幅について何も示さない欄は3.3.1の問題として、ブラウザーによる入力チェックが有効なフォームで `pattern` が番号をはじき、欄の近くに幅を示す言葉もない欄は3.3.3の問題として報告します。診断はフォームを送信しないため、ページの日本語の部分にあるほかの電話番号、郵便番号、フリガナの欄は、人が試す項目として挙げます。

## 修正前：番号が正しくないとだけ伝える確認

```html
<form>
  <label for="tel">電話番号</label>
  <input id="tel" name="tel" type="tel" autocomplete="tel"
    aria-describedby="tel-error">
  <p id="tel-error"></p>
  <button type="submit">次へ</button>
</form>
```

```js
const form = document.querySelector('form');
const tel = document.getElementById('tel');
const error = document.getElementById('tel-error');

form.addEventListener('submit', (event) => {
  const valid = /^[0-9]{10,11}$/.test(tel.value);
  tel.setAttribute('aria-invalid', String(!valid));
  error.textContent = valid ? '' : '正しい電話番号を入力してください。';
  if (!valid) event.preventDefault();
});
```

確認は半角の0〜9しか受け付けないため、０１２３４５６７８９は通りません。メッセージは番号が正しくないと言うだけで、半角の数字なら通ることを伝えていません。

## 修正前：0〜9以外を削除するハンドラー

```html
<form>
  <label for="tel">電話番号</label>
  <input id="tel" name="tel" type="tel" autocomplete="tel"
    aria-describedby="tel-error">
  <p id="tel-error"></p>
  <button type="submit">次へ</button>
</form>
```

```js
const form = document.querySelector('form');
const tel = document.getElementById('tel');
const error = document.getElementById('tel-error');

// 欄を数字だけに保つ。
tel.addEventListener('input', () => {
  tel.value = tel.value.replace(/[^0-9]/g, '');
});

form.addEventListener('submit', (event) => {
  const valid = /^[0-9]{10,11}$/.test(tel.value);
  tel.setAttribute('aria-invalid', String(!valid));
  error.textContent = valid ? '' : '正しい電話番号を入力してください。';
  if (!valid) event.preventDefault();
});
```

同じフォームに、ハンドラーを1つ足したものです。全角数字はどれも0〜9以外の文字なので、入力されるとすぐにハンドラーが削除し、何が起きたのか、なぜなのかは、ページのどこにも示されません。

## 修正後：数字を変換してから確認する

```html
<form>
  <label for="tel">電話番号</label>
  <input id="tel" name="tel" type="tel" autocomplete="tel"
    aria-describedby="tel-error">
  <p id="tel-error"></p>
  <button type="submit">次へ</button>
</form>
```

```js
const form = document.querySelector('form');
const tel = document.getElementById('tel');
const error = document.getElementById('tel-error');

form.addEventListener('submit', (event) => {
  // 全角の数字とハイフンを半角にし、スペースとダッシュを取り除く。
  const digits = tel.value.normalize('NFKC').replace(/[\s\-‐−ー]/g, '');
  const valid = /^[0-9]{10,11}$/.test(digits);
  tel.setAttribute('aria-invalid', String(!valid));
  error.textContent = valid ? '' : '電話番号を10桁か11桁の数字で入力してください。例：0123456789';
  if (valid) tel.value = digits;
  else event.preventDefault();
});
```

`normalize('NFKC')` はUnicodeの互換正規化で、全角の数字、全角のハイフン、全角のスペースを半角にします。０１２３４５６７８９は0123456789になります。続く置換で、スペースと、区切りに入力されるダッシュを取り除きます。正規化では変わらない「ー」もここで取り除きます。その後で番号を確認するため、同じ番号はどちらの幅でも通り、欄には変換した数字が入り、フォームはその数字を送信します。9桁の番号のように、それでも正しくない番号には、何を入力すればよいかをメッセージで伝えます。`input` のハンドラーはないため、入力中に文字が消えることはありません。リクエストはこのスクリプトを通らずにサーバーに届くこともあるため、サーバーでも同じように変換してください。

## 修正後：どの数字で入力するかを伝える

```html
<form>
  <label for="tel">電話番号</label>
  <input id="tel" name="tel" type="tel" autocomplete="tel"
    aria-describedby="tel-error">
  <p id="tel-error"></p>
  <button type="submit">次へ</button>
</form>
```

```js
const form = document.querySelector('form');
const tel = document.getElementById('tel');
const error = document.getElementById('tel-error');

form.addEventListener('submit', (event) => {
  const valid = /^[0-9]{10,11}$/.test(tel.value);
  const wide = /[０-９]/.test(tel.value);
  tel.setAttribute('aria-invalid', String(!valid));
  error.textContent = valid ? ''
    : wide ? '電話番号に全角の数字が含まれています。半角の数字（0〜9）で入力してください。'
    : '電話番号を10桁か11桁の数字で入力してください。例：0123456789';
  if (!valid) event.preventDefault();
});
```

確認はそのままですが、メッセージで、何が問題で何をすればよいかを伝えます。番号に全角の数字が含まれていること、欄が求めているのは半角の数字であることです。入力中に文字を削除するハンドラーはなくしたため、数字は見える場所に残り、メッセージでその理由が分かります。両方の達成基準を満たしますが、番号ははじかれたままで、利用者は日本語入力をオフにして入力し直す必要があります。「半角数字」のように、ラベルのそばにも書いておけば、最初からその形で入力できます。

## どの方法を選ぶか

どちらも3.3.1と3.3.3を満たします。使うべきなのは、番号を変換する方法です。番号はもともと正しく、変換に必要なコードは1行です。2つ目の方法も全角数字に気づくために1行を書いており、その1行を番号の変換ではなくメッセージに使っています。どの数字で入力するかを伝える方法は、ほかの値を受け付けないシステムにそのまま送るフォームのように、欄の決まりを変えられない場合に使います。ただし、日本語で入力する人は全員、番号を2回入力することになります。

## 実装の判断

変換は、フォームを送信するときか、欄からフォーカスが外れたときに行い、`input` のハンドラーでは行いません。IMEで変換している間、入力中の文字はIMEが扱っています。その間に値を書き換えると、利用者の入力を妨げます。入力のたびに半角へ直す場合は、「[IMEの変換確定のEnterで送信される入力欄](/ja/learn/ime-composition)」のキー操作のハンドラーと同じように、`event.isComposing` がtrueのイベントを飛ばしてください。

1つ目の修正は、変換した数字を欄に戻すため、フォームは保存するのと同じ形で番号を送信します。確認画面などで番号を利用者に見せるときは、本人が分かる形にしてください。GOV.UKの電話番号のパターンは、自分の入力した番号が書き換えられて表示されると、利用者が戸惑ったと報告しています。

ブラウザー自身に確認させる `pattern` が欄にある場合、ブラウザーはスクリプトが変換する前の、入力されたままの値を調べます。`pattern` で両方の幅を受け付けて（`[0-9０-９]{10,11}`）サーバーで変換するか、`pattern` は付けずに、送信時のハンドラーで変換してから確認します。0〜9だけを受け付ける `pattern` では、メッセージがブラウザーの汎用的な「指定されている形式で入力してください」に変わるだけで、1つ目の修正前に戻ります。

`NFKC` は、電話番号、郵便番号、カード番号のように、書き方をこちらで決める欄にだけ使います。正規化が変えるのは幅だけではなく、①は1に、㈱は(株)になります。名前や住所では、利用者が選んだ文字が変わってしまいます。サーバーでも、Pythonの `unicodedata.normalize`、Javaの `Normalizer`、PHPの `Normalizer`（intl拡張）で同じNFKCが使えます。

`type="tel"` や `inputmode="numeric"` は、スマートフォンで半角の数字を入力するキーパッドを出します。それでも、貼り付けた番号や、パソコンで日本語入力のまま入力した番号は全角のことがあるため、欄での変換は必要です。

## 修正の確認

1. 日本語入力をオンにし、ひらがなモードのまま、電話番号を全角の数字で入力してフォームを送信する。受け付けられるか、半角数字で入力するようメッセージで伝えられる。
2. ひらがなモードのまま、区切りにハイフンを入れて入力し直す。ハイフンは「ー」になる。受け付けられるか、代わりに何を入力すればよいかがメッセージで伝えられる。
3. 欄に０１２３４５６７８９を貼り付けてフォームを送信する。送信するまで数字は欄に残り、結果は手順1と同じになる。
4. 9桁の番号を入力してフォームを送信する。何桁で入力すればよいかがメッセージで伝えられる。
5. スクリプトを無効にするか、テスト用のリクエストで、ページのスクリプトを通さずに全角の番号をサーバーへ送る。サーバーが変換するか、幅を明記したメッセージではじく。
6. スクリーンリーダーを起動し、全角の番号を送信する。欄にフォーカスすると、メッセージが一緒に読み上げられ、何を直せばよいかが分かる。

OS、ブラウザー、IME、支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通のフォームの欄やその入力チェックを変更した際は、再度確認します。

## 制限

このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。例は桁数の決まった電話番号の欄1つです。郵便番号、カード番号、金額には、変換の後にそれぞれの決まりが必要で、国番号付きの番号では「+」を残す必要があります。置換で取り除くのはスペースとダッシュで、括弧は残るため、03（1234）5678のように書いた番号にはメッセージが出ます。利用者が括弧を使う場合は、置換の対象に加えてください。ひらがなや半角カタカナをはじくフリガナの欄も、同じ問題です。NFKCは半角カタカナを全角にし、ひらがなはUnicodeで決まった数だけずらせばカタカナになりますが、フリガナの欄を扱うガイドはまだありません。送信に失敗した後、フォーカスの移動やエラーの一覧でメッセージをどう届けるかは、「[直し方がわからないフォームエラー](/ja/learn/accessible-form-errors)」で扱っています。

---

このガイドをコーディングエージェントで使う: MCP https://easeweb.dev/mcp（設定は https://easeweb.dev/ja/services/developers）
