HTML5におけるinput要素のpattern、type属性のおさらい

おさらいシリーズ第4弾w

HTML5ではinput要素が拡張されて正規表現による入力値バリデーションができるようになっている。利用するには、 `<input pattern="">` のように `pattern` 属性値にバリデーション用の正規表現を書いてやればよい。

──で、毎度UI作っていて正規表現パターンを調べるのが手間だったので、定形的な入力値については一度まとめておこうかと思った次第。
まぁ、正規表現の自分用チートシートみたいなものですな。

## pattern属性用正規表現サンプル集

| バリデーション内容 | 正規表現（pattern属性） | 備考 |
|------------------|------------------------|----------------|
| 3文字以上 | `.{3,}` | バイト単位ではなく、半角・全角ともに1文字となる |
| 半角英数字 | `^[0-9A-Za-z]+$` |  |
| 半角英数字6文字以上 | `^([a-zA-Z0-9]{6,})$` |  |
| 半角英字8文字 | `[A-Za-z]{8}` |  |
| ファイル名チェック | `^[-0-9a-zA-Z_.]+$` | マルチバイト文字を含むファイル名は非対応 |
| パスワード用 | `(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}` | 8文字以上で1文字以上の数字、小文字アルファベット、大文字アルファベットがそれぞれ含まれていること |
| 郵便番号 | `\d{3}-\d{4}` | [※1](#notices) |
| 電話番号 | `\d{2,4}-\d{3,4}-\d{3,4}` | [※2](#notices) |
| メールアドレス | `[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$` | [※3](#notices) |
| URL | `https?://.+` | [※4](#notices) |
| 全角カタカナ | `^[ァ-ンヴー]+$` , `[\u30A1-\u30FF]*` | 出力されるHTMLがUTF-8である必要あり[※5](#notices) |
| 半角カタカナ | `[\uFF66-\uFF9D]*` | 出力されるHTMLがUTF-8である必要あり |
| 全角ひらがな | `^[ぁ-ん]+$` , `[\u3041-\u309F]*` | 出力されるHTMLがUTF-8である必要あり[※6](#notices) |
| 全角数字 | `[\uFF10-\uFF19]*` | 出力されるHTMLがUTF-8である必要あり |
| 全角アルファベット | `[\uFF21-\uFF3A｜\uFF41-\uFF5A]*` | パイプ「｜」のところは半角で |
| 半角英数字以外 | `[^\x20-\x7E]*` |  |
| 日時（Y-M-d H:i:s) | `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}` | [※7](#notices) |
| 正の整数 | `[1-9][0-9]*` | [※8](#notices) |
| `'`や`"`のクォーテーションを許可しない | `[^\x22\x27]*` | 検索フォームのバリデーションに適しているかも |
| IPアドレス | `^\d{1,3}(\.\d{1,3}){3}$` | [※9](#notices) |
| 浮動小数点 | `^-?\d{1,}\.\d*$` |   |


> ###### Notices
>
> ※1 ハイフンありなしを許容する場合は `\d{3}-?\d{4}` のようにする
> ※2 国番号付きを許容する場合は `^(+|)\d{2,4}-\d{3,4}-\d{3,4}` のようにする
> ※3 もっと厳密にバリデーションしたい時は `^[a-zA-Z]{1}[0-9a-zA-Z]+[\w\.-]+@[\w\.-]+\.\w{2,}$` こんなのが良いかも
> ※4 もっと厳密にバリデーションしたい時は `^http(|s)://[0-9a-zA-Z/#&?%\.\-\+_=]+$` こんなのも良いかも
> ※5 全角空白と半角空白を許容する場合は `[ァ-ンヴー|　| ]+$` のようにする
> ※6 長音記号「ー」はカタカナだが、許容したい場合は、`\u30FC` をパターンマッチさせる
> ※7 もっと精密に、 `([1-2]{1}[0-9]{3})\-([0-1][0-9])\-([0-3][0-9]) (0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}` こんなのも良いかも
> ※8 0を許容するなら `(0|[1-9][0-9]*)` とする
> ※9 厳密にチェックするなら `^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` が良い（な、長い…）

ちなみに、input要素すべてでpattern属性が有効になるわけではなく、input要素のtype属性の種類とブラウザによって有効対象が異なる。調べたところ、その対応表は下記の通りだった。
ちなみに、調べた各PCブラウザのバージョンは2015年7月14日時点の最新バージョンで、AndroidはOS 4.2.2、iPhoneは持ってないので不明である。

### pattern属性が有効なtype属性

| type属性 | chrome | Firefox | IE11 | Safari | Opera | Android |
|:----:|:-:|:-:|:-:|:-:|:-:|:-:|
| text | ○ | ○ | ○ | × | ○ | × |
| search | ○ | ○ | ○ | × | ○ | × |
| url | ○ | ○ | ○ | × | ○ | × |
| email | ○ | ○ | ○ | × | ○ | × |
| password | ○ | ○ | ○ | × | ○ | × |
| datetime | ○ | ○ | ○ | × | ○ | × |
| date | × | ○ | ○ | × | × | × |
| month | × | ○ | ○ | × | × | × |
| week | × | ○ | ○ | × | × | × |
| time | × | ○ | ○ | × | × | × |
| datetime-local | × | ○ | ○ | × | × | × |
| number| × | × | × | × | × | × |
| range | × | × | × | × | × | × |
| color | × | × | ○ | × | × | × |

うーん、Safari（Windows版）と、同じWebkitブラウザであるAndroid標準ブラウザではPattern属性が使えなかった…。おそらく同じWebkit系ブラウザのiPhoneでも一緒だと思われる。
やっぱり難儀なクロスブラウザの壁…。


## type属性によるバリデーション

さて、もうひとつ。HTML5でのinput要素の拡張は結構多くて、前述のpattern属性を使わなくても、type属性に入力候補値の型を適切に指定することで、入力フィールド自体のUIが最適化されるようになってもいる。HTML5の黎明期にはこのフィールド最適化に対応しているブラウザはOperaぐらいしかなかったんだが、そろそろ他のモダンブラウザも対応されているかなぁ…と思って調べてみた。


| type値 | Chrome | Firefox | IE11 | Safari | Opera | Android |
|:------:|:-------|:--------|:-----|:-------|:------|:--------|
| text | - | - | - | - | - | - |
| search | オートコンプリートが有効なフィールド | text と同じ | text と同じ | 入力値クリア用のXボタンが付く | オートコンプリートが有効なフィールド | オートコンプリートが有効なフィールドになり実行ボタンが検索ボタンになる |
| url | `[a-zA-Z]{1,}:.*` | `[a-zA-Z]{1,}:.*` | `.{1,}:.*` | text と同じ | `[a-zA-Z]{1,}:.*` | text と同じ |
| tel | text と同じ | text と同じ | text と同じ | text と同じ | text と同じ | 電話番号に利用できる文字しか入力できない |
| email | `.{1,}@.*` | `.{1,}@.*` | `.{1,}@.*` | `.{1,}@.*` | `.{1,}@.*` | text と同じ |
| password | 半角英数記号のみ | 半角英数記号のみ | 半角英数記号のみ | 半角英数記号のみ | 半角英数記号のみ | 半角英数記号のみ |
| datetime | text と同じ | text と同じ | text と同じ | スピンボックス | text と同じ | text と同じ |
| date | Dateピッカー（カレンダー付） | text と同じ | text と同じ | スピンボックス | Dateピッカー（カレンダー付） | text と同じ |
| month | Dateピッカー（カレンダー付） | text と同じ | text と同じ | スピンボックス | Dateピッカー（カレンダー付） | text と同じ |
| week | Dateピッカー（カレンダー付） | text と同じ | text と同じ | スピンボックス | Dateピッカー（カレンダー付） | text と同じ |
| time | タイムフィールド（スピンボックス型） | text と同じ | text と同じ | スピンボックス | タイムフィールド（スピンボックス型） | text と同じ |
| datetime-local | Dateピッカー（カレンダー付）＋タイムフィールド | text と同じ | text と同じ | スピンボックス | Dateピッカー（カレンダー付）＋タイムフィールド | text と同じ |
| number | スピンボックス | スピンボックス | `^[-+]{,1}\d.*` (※) | スピンボックス | スピンボックス | 数値固定フィールド |
| range | レンジバー | レンジバー | レンジバー | レンジバー | レンジバー | レンジバー |
| color | カラーピッカー | カラーピッカー | text と同じ | text と同じ | カラーピッカー | text と同じ |

> ※ IE11の `type="number"` のバリデーションはバグのような仕様。先頭が数値ならあとは文字列でも受け入れてしまう…これはひどい。

chromeとOperaのtype属性によるフィールド最適化はすばらしくリッチなのだが、ブラウザごとにここまで実装に差があると、やはりtype属性だけに頼ったUIではダメなことがわかる。また、email（`a@a`で通ってしまう）やurl(`1:.`で通ってしまう)など、type属性でバリデーションされる正規表現が貧弱すぎて、およそ役に立たないということもある。今のところ信頼して使えるのは `type="range"` だけだ。

うーん、この辺はブラウザ各社が足並みを揃えて実装して欲しいもんだなぁ…。

## 結論として…

だいだい予想はしていたんだが、HTML5のinput要素によるバリデーションはまだまだ使い物になるレベルではないということがわかった。
まぁ、フロントエンドで入力値のバリデーションがしっかりやられていたとしても、バックエンドに値が渡った時点でまたしっかりバリデーションするのは基本だ（クロスサイトスクリプティング攻撃にはHTML＋ブラウザでのバリデートなんてほぼ無力だからね）。
…でも、そうなんだけど、少しでもバックエンド側のリスクが下がるならもうちょっとフロントエンド側でもしっかりバリデーションができるようになってくれると嬉しいもんだ。

今のところ、最低限input要素のtypeとpattern属性は入力候補値に合わせて最適化しておくと、まぁ気休め程度にはなるのかな…w