[JavaScript]HTML5 Form Validationの制御と注意事項

##**概要**
HTML5から支援している**Form Validation**、皆さんご存知ですよね。
非常に便利ですし、自分もHTML5に感謝しています。👏
サーバでは常にデータを検証するべきですが、追加のデータ検証をWebページ自身で行うことにも多くの利点があります。ユーザがフォームに入力している間にデータを検証することで、ユーザは何らかのミスをしたことを直ちに知ることができます。これはユーザが HTTP のレスポンスを待つ時間を減らし、またサーバで誤ったフォーム入力を扱うことがないようにします。

詳しい内容は以下の記事をご参考してください。
[🔗[JavaScript]HTML5 Form Validation](http://qiita.com/leedohyung-dba/items/ffa06bbb4f3da64a8086)

**しかし、ドンー (OA O; )**
ブラウザー別にメッセージが統一されず、実際の案件には、デメリットになっちゃったんです。
やっぱりHTML5 Form Validationに完全に依存されると気持ち悪いですね。

で、この記事ではHTML5 Form Validationのメッセージを
直接定義してみた内容を書いておきたいと思っております。

意外と**簡単**にできますが、**いくつかのケースを確認して見ると変な状態になり**ます。
もし、他の記事に例を出しているコードをそのまま使って良い結果確認しても、
**絶対色んなケースをしっかり確認するべき**です。
そうしないと、**リリース後バグが出ちゃってしまうかもしれません**。

これらの自動メッセージには、欠点が 2 つあります。
① CSS でメッセージのルックアンドフィールを変更するための標準的な方法がありません。
② メッセージはブラウザのロケールに依存しており、ある言語のページでエラーメッセージが別の言語で表示されることがあります。
ちなみにこれらのメッセージの外見やテキストを変更するには、
**JavaScriptを使用しなければなりません。**
HTMLやCSS だけで変更する方法はありません。
※①番を対応するためには、※[🔗MDN(Mozilla Developer Network)](https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Forms/Data_form_validation)の内容をご参考お願いします。
※②番は本記事を参号しても良いと思います。👏

また、せっかくなので、JavaScriptライブラリーを構築してnpmに上げました。
その内容は以下の記事をご覧ください。
🔗[JavaScript]HTML Form Validation コントローラー ライブラリー fvc (準備中)

##**HTML5 Form Validationのメッセージの変更**
ネイティブのエラーメッセージを制御したい場合や、**HTML5のフォーム検証をサポートしないブラウザに対処したい場合は、JavaScriptを使用するしかありません。**
で、HTML5 では、フォーム要素の状態を確認したりカスタマイズしたりするための [🔗constraint validation API](https://www.w3.org/TR/html5/forms.html#the-constraint-validation-api)を提供します。**特に、エラーメッセージのテキストを簡単に変更できます。**

####✪Constraint validation API のプロパティ
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/c1acf696-5f93-ebe9-7a77-72ba7ddaa2cb.png)

####✪Constraint validation API のメソッド
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/026738fd-e6df-5916-ac47-d9d2cdff0241.png)

`elem.validity`の中身を以下のように取得できます。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/ade7fa0a-7c26-83f0-1ee7-fea189ee0996.png)
**しかし、このオブジェクトの中身は変更できません。**
`Uncaught TypeError: Cannot assign to read only property 'typeMismatch' of object '#<ValidityState>'`
なので、内部のオブジェクト要素の値を変更して強制でエラーを発生したり、
エラーを無効化するのは**不可能**です。

**Constraint validation API**のプロパティとメソッドだけを見たらどうやってメッセージを変更するかを
イメージが思い出していらっしゃると思います。
**しかし、🔥ご注意してください。実際、ネットの色んな記事にあるコードを入れて変更されたメッセージのみを確認してそのままリリースしてしまうケースが結構あるらしいです。**

以下の例を確認しましょう。
メッセージは変わりますが、変な状況があります。

例①

```js:bug_ex_1.js
const VALID_MESSAGE_REQUIRED = "入力してくださいね-!✋";
const VALID_MESSAGE_PATTERN = "正しい構文で入力してくださいね-!✋";
const VALID_MESSAGE_TYPE = "指定されている形式で入力してくださいね-!✋";

$("input").each(function(index, elem) {
    elem.addEventListener("invalid", function(e) {
        if(elem.validity.valueMissing){
            //要素が入力必須のフィールドであるのに値がない場合
            e.target.setCustomValidity(VALID_MESSAGE_REQUIRED);
        } else if(elem.validity.typeMismatch) {
            //要素の値が正しい構文ではない場合
            e.target.setCustomValidity(VALID_MESSAGE_TYPE);
        } else if (elem.validity.patternMismath) {
            //要素の値が与えられたパターンにマッチしない場合
            e.target.setCustomValidity(VALID_MESSAGE_PATTERN);
        }
    });
});
```
本例はelem要素の**invalid**イベントにリスナーを付けて処理しています。
正しく、メッセージが設定した内容とおり変わります。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/6251423f-9cbb-60b1-5ad8-9cc74d30d1d2.png)

しかし、正しく入力してもフォーム検証に掛かってしまいます。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/1cf29725-0478-fb8c-fa97-63518dfee783.png)

この原因は独自のエラーメッセージを設定しても要素が不正じゃなくても指定したエラーが表示されます。
その理由は要素の**validityの中身を見ると理解でき**ます。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/f7f6b773-c40e-cd11-91e0-7eaab6e76665.png)
👆はメール形式ではない場合、要素の**validity**状態です。
メール形式が間違ったから**typeMismatch**が**true**ですし、
フォーム検証に掛かったので、**customError**が**true**でございます。
しかし、以下の状態はちょっと違います。
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/10836f2d-eeb5-ab6b-1f93-710b791a2bce.png)
👆はメール形式も他の検証にも問題無い場合の**validity**状態です。
しかし、**customError**がまだ**true**でございます。
なので、検証にずっと掛かってしまいます。
上端にも説明しましたが、**validityオブジェクト**の要素の値を変更するのはできません。
`elem.validity.customError = false;`
![image](https://qiita-image-store.s3.amazonaws.com/0/108364/fe466217-1e00-a2a2-7b8d-3597e5a807cd.png)

**🌟対応方法はConstraint validation APIのメソッドsetCustomValidityに引数が空文字列を入れます。**
>引数が空文字列である場合は、独自のエラーがクリアされます。

```js:bug_ex_1.js
const VALID_MESSAGE_REQUIRED = "入力してくださいね-!✋";
const VALID_MESSAGE_PATTERN = "正しい構文で入力してくださいね-!✋";
const VALID_MESSAGE_TYPE = "指定されている形式で入力してくださいね-!✋";

$("input").each(function(index, elem) {
    elem.addEventListener("invalid", function(e) {
        if(elem.validity.valueMissing){
            //要素が入力必須のフィールドであるのに値がない場合
            e.target.setCustomValidity(VALID_MESSAGE_REQUIRED);
        } else if(elem.validity.typeMismatch) {
            //要素の値が正しい構文ではない場合
            e.target.setCustomValidity(VALID_MESSAGE_TYPE);
        } else if (elem.validity.patternMismath) {
            //要素の値が与えられたパターンにマッチしない場合
            e.target.setCustomValidity(VALID_MESSAGE_PATTERN);
        } else {
            e.target.setCustomValidity("");
        }
    });
});
```
👆の例でフォーム検証に掛かって、正しく合わせるとエラーにならないです。
が・・・まだこれでは他の部分に変な状況が起きます。
大変申し訳ございませんが、予想したより、長くなって・・・
こちらの原因を上端の内容をご参考して把握してみるといかがでしょうか？！ ~(>ㅁ <;;)
一番下端に色んなケースにも上手く動作するコードは書いて置きましたので、 
時間が無い方はすぐ下端にLet's go!!! : )

例②

```js:bug_ex_2.js
const VALID_MESSAGE_REQUIRED = "入力してくださいね-!✋";
const VALID_MESSAGE_PATTERN = "正しい構文で入力してくださいね-!✋";
const VALID_MESSAGE_TYPE = "指定されている形式で入力してくださいね-!✋";

$("input").each(function(index, elem) {
    elem.addEventListener("keyup", function (event) {
        if(elem.validity.valueMissing){
            elem.setCustomValidity(VALID_MESSAGE_REQUIRED);
        } else if(elem.validity.typeMismatch) {
            elem.setCustomValidity(VALID_MESSAGE_TYPE);
        } else {
            elem.setCustomValidity("");
        }
    });
});
```
👆の例は全然問題ありませんが、
必須入力のフォーム検証項目には最初エラーメッセージが設定通り変わらない場合があります。

普通、必須入力のフォーム検証に掛かるのは、
その項目にカーソルを置かなかったケースが普通ですよね。
なので、最初一回、必須入力項目要素にメッセージを設定する必要があります。
また、制御されない`<input>`タグのためは、条件制御が必要です。

ということで、上手く動作するコードは以下でございます。
ご参考してくださいと嬉しいです！

例③

```js:success_ex_3.js
const VALID_MESSAGE_REQUIRED = "入力してくださいね-!✋";
const VALID_MESSAGE_PATTERN = "正しい構文で入力してくださいね-!✋";
const VALID_MESSAGE_TYPE = "指定されている形式で入力してくださいね-!✋";

$("input").each(function(index, elem) {
    if(elem.validity.valueMissing){
        elem.setCustomValidity(VALID_MESSAGE_REQUIRED);
    }
    elem.addEventListener("keyup", function (event) {
        if(elem.validity.valueMissing){
            elem.setCustomValidity(VALID_MESSAGE_REQUIRED);
        } else if(elem.validity.typeMismatch) {
            elem.setCustomValidity(VALID_MESSAGE_TYPE);
        } else {
            elem.setCustomValidity("");
        }
    });
});
```

##**まとめ**
ウェブの技術がどんどんより便利になっていますが、
エンジニアが制御及び拡張出来るかに注目しましょう。
また、導入した技術はしっかりテストしましょう！
以上、ご覧くださてありがとうございました。 👏