TL;DR
以下の正規表現は、HTML 仕様のメールアドレス正規表現の末尾に、 TLD 必須のルールを加えたものです。
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/
<input type="email">
の pattern
属性に指定する場合は、以下のように短く書けます。
<input type="email" pattern=".+\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]">
以下、詳細です。
HTML 仕様のメールアドレス正規表現について
HTML 仕様には、 <input type="email">
のバリデーションルールと同等の正規表現が含まれています。
以下がその正規表現です。
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
ブラウザが標準で提供する機能なので、ぜひこのルールを活用したいところです。
課題
しかしながら。。
この type="email"
は、TLD が無いメールアドレスを許可します。a@a
は、valid なアドレスだと判定されます。
上の正規表現の遷移図は以下のようになっています。
(Regexper)
このような緩いルールになっている理由は、pattern
属性を使って更に条件を狭めることができるからです。デフォルトでメールアドレスとして最も緩い条件を適用し、次に pattern
で必要に応じた制限を加えます。
If you need the entered e-mail address to be restricted further than just "any string that looks like an e-mail address," you can use the pattern attribute to specify a regular expression the value must match for it to be valid.
(MDN)
デフォルトの正規表現と pattern
の正規表現は、AND 条件です。両方が検証されます。
However, the browser runs both the standard e-mail address filter and our custom pattern against the specified text.
(MDN)
2つのルールは AND で検証されるので、pattern
属性で、例えば @
が含まれるかどうかをチェックする必要はありません。
pattern
属性を指定しない type="email"
は、現実には使い物になりません。TLD があっても無くてもどちらでも構わないようなメールアドレスを input
で入力させるのは、相当に特殊な場合だけでしょう。
JPNIC の「ドットなしドメイン名 (Dotless Domain Names) について」の記事も参考になります。
正規表現に TLD の制限を加える
そこで、HTML 仕様を極力変えずに TLD の制限を加えてみます。
(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*
がドメインの2番め以降のラベルで、0回以上の繰り返しなので、この後に、以下を付け加えます。
\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]
1文字の TLD を禁止する仕様は存在しないようですが、今の TLD に1文字のものはないので、(?: )?
を外して「TLD 2 文字以上」の制限を加えています。上限 63 文字は、他のラベルと同様です。
修正を加えた正規表現は以下の通り。
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/
遷移図は以下のようになります。
(Regexper)
TLD 部分だけを pattern に指定できるか
通常の正規表現であれば、部分一致で、末尾の TLD の正規表現だけを指定できそうなものです。
pattern="\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"
しかし、これはうまくいきません。HTML の pattern
属性は、^(?: )$
でラップされるからです。
5 . Let
anchoredPattern
be the string"^(?:"
, followed bypattern
, followed by")$"
.
(HTML Living Standard)
なので、pattern
にはメールアドレス全体にマッチする正規表現を指定する必要があります。
.+
を先頭につけて、
pattern=".+\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]"
と書けます。デフォルトのルールと pattern
は両方が検証されるので、前半部分は .+
と省略してしまっても問題ありません。
ローカルパートに記号を許すか
デフォルトの正規表現では、以下の記号をローカルパート (@
より前) に許可しています。
.!#$%&'*+/=?^_`{|}~-
この内、絶対に必要なのは以下でしょう。
.+_-
残りの記号は、場合によっては削ってもよいかもしれません。
!#$%&'*/=?^`{|}~
特にユーザ登録で、他のサービスと連携する可能性がある場合、最初から絞っておかないと後で困る可能性がありそうです。こちらで許可した記号が、連携先では許可されていないかもしれません。
メジャーなサービスのバリデーションはどうなっているか
いくつかのサイトで、ユーザ登録画面の挙動を見てみました。(実際には登録せず、クライアントサイドのバリデーションの挙動だけを見ています)
- Facebook は、1 文字の TLD は許可。ローカルパートの記号も許可しているようでした
- Twitter は、1文字の TLD は禁止。記号は基本の文字に加えて
&
を許可していました - Apple ID は、1文字の TLD は禁止。基本の記号だけを許可しているようでした
デモ
以下で type="email"
の input
要素に pattern
を指定したときの挙動を試せます。
See the Pen HTML email validation by koseki (@kkoseki) on CodePen.
また、様々なメールアドレスに正規表現を適用するテストページをこちらに作成しました。
Microsoft の以下のページに、いい感じのテストデータがあります。