【STEP.29】
フォーム要素の操作・検証方法をマスターしよう!

フォームとは

クライアントサイドJavaScriptにおいて、フォームはエンドユーザーからの入力を受け取る代表的な手段です。

Webページで利用できる主なフォーム要素(入力要素)を以下にまとめます。

*」の付いている要素はHTML5で追加されたものです。

ブラウザーによっては対応できていないものもありますが、その場合にも標準的なテキストボックスが表示されるだけで、特に害があるわけではありません。

できるだけ目的に応じたフォーム要素を利用することをおすすめします。

【HTMLで利用できる主なフォーム要素】
(※<textarea>/<select>要素以外は、<input type="XXX">のXXXに相当します。)
email/search/tel/text
text/*email/
*search/*tel
textarea
<textarea>
number
*number
password
password
range
*range
time
*time
checkbox
checkbox
radio
radio
date
*date
datetime-local
*datetime-local
month
*month
week
*week
file
file
select
<select>
select(multiple)
<select>(multiple)
color
*color

以下では、これらフォーム要素の操作方法について解説していきます。

テキストボックス/テキストエリアの値を取得/設定する

valueプロパティを利用します。

<form>
  <div>
    <label for="name">氏名:</label>
    <input id="name" name="name" type="text" size="20" />
  </div>
  <div>
    <label for="profile">プロフィール:</label><br />
    <textarea id="profile" name="profile" rows="4" cols="30"></textarea>
  </div>
  <input id="btn" type="button" value="送信" />
</form>
let name = document.getElementById('name');

let profile = document.getElementById('profile');

document.getElementById('btn').addEventListener('click', function(){
  console.log(name.value);
  console.log(profile.value);
}, false);

値を設定するならば、同じくvalueプロパティに値を代入します。

name.value = '横浜流星';

選択ボックスの値を取得/設定する

valueプロパティを利用します。

<form>
  <label for="season">四季:</label>
  <select id="season" name="season">
    <option value="spring">春
    <option value="summer">夏
    <option value="autumn">秋
    <option value="winter">冬
  </select>
</form>
let season = document.getElementById('season');

// 選択ボックス変更時に、その値を取得
season.addEventListener('change', function(){
  console.log(season.value);
}, false);

値を設定するならば、同じくvalueプロパティに値を代入します。

season.value = 'autumn';

ラジオボタンの値を取得する

個々のラジオボタンが選択されているかどうかは、checkedプロパティで判定できます。

ただし、一般的には、ラジオボタンはグループで利用するものです。

実用途としては、グループ内のラジオボタンを順に走査し、いずれが選択されているかどうかを判定したうえで、選択されたものの値を取得するというコードを書く必要があります。

以下はそのためのコード例です。

<form>
  <div>
    お使いのOSは?
    <label>
      <input type="radio" name="os" value="windows" />Windows
    </label>
    <label>
      <input type="radio" name="os" value="mac" />Mac OS
    </label>
    <label>
      <input type="radio" name="os" value="unix" />Unix
    </label>
    </div>
    <input id="btn" type="button" value="送信" />
 </form>
let getRadioButton = function (name) {
  let result = '';
  // 指定されたname属性のラジオボタンを取得
  let elems = document.getElementsByName(name);
  // ラジオボタンを順に走査し、選択状態にあるかを判定
  for (let i = 0; i < elems.length; i++) {
    if (elems[i].checked) {
      result = elems[i].value;
    }
  }
  return result;
}
// [送信]ボタンクリックでラジオボタンの値を取得
document.getElementById('btn').addEventListener('click', function () {
  console.log(getRadioButton('os'));
}, false);

ラジオボタンのように、name属性が同じ要素群を取得するには、getElementsByNameメソッドを利用します。

取得した要素群(NodeList)は、for命令で順に取り出せます。

あとは、checkedプロパティでラジオボタンがチェックされているかどうかを確認し、チェックされている場合には、そのvalueプロパティの値を返します。

ラジオボタンではvalueプロパティは選択の有無に関わらずvalue属性の値を返します。

チェックボックスの値を取得する

チェックボックスが選択されているかどうかは、checkedプロパティで判定できます。

ただし、チェックボックスが単一/複数いずれであるかによってコードも変化するので、それぞれについてコード例を見ていきます。

チェックボックスが単一の場合

チェックボックスをオン/オフを表すような用途で利用する場合です。

これには、checkedプロパティを参照するだけで良いので、シンプルです。

<form>
  <div> 
  お知らせメールを希望しますか?:<br />
  <label>
    <input id="mail" type="checkbox" name="mail" value="お知らせ希望" />希望する
  </label>
  </div>
</form>
let mail = document.getElementById('mail');
	
// チェックボックス変更時に、その値に応じてログを表示
mail.addEventListener('change', function () {
  if (mail.checked) {
    console.log(mail.value);
  } else {
    console.log('お知らせは、配信されません'); 
  }	
}, false);

チェックボックスが複数の場合

チェックボックスを複数選択リストとして利用する場合には、以下のコードを書きます。

<form>
  <div>
  お使いのOSは?
  <label>
    <input type="checkbox" name="os" value="windows" />Windows
  </label>
  <label>
    <input type="checkbox" name="os" value="mac" />Mac OS
  </label>
  <label>
    <input type="checkbox" name="os" value="unix" />Unix
  </label>
  </div>
  <input id="btn" type="button" value="送信" />
</form>
let getCheckbox = function (name) {
  // 選択値を格納するための配列を準備
  let result = [];
  // 指定されたname属性のチェックボックスを取得
  let elems = document.getElementsByName(name);
  // チェックボックスを順に走査し、選択状態にあるかを判定
  for (let i = 0; i < elems.length; i++) {
    if (elems[i].checked) {
      result.push(elems[i].value);
   }
  }
  return result;
}
// [送信]ボタンクリックでチェックボックスの値を取得
document.getElementById('btn').addEventListener('click', function () {
  console.log(getCheckbox('os'));
}, false);	

ラジオボタン/チェックボックスの値を設定する

ラジオボタン/チェックボックスの値を設定するには、以下のような手順を踏みます。

  • 目的の要素(群)を取得
  • for命令で個々の要素を走査
  • 要素のvalue属性と、設定したい値とを比較し、等しいもののcheckedプロパティをtrueに設定

以下に、それぞれの具体的なコードを掲載します。

ラジオボタンの設定

<form>
  <div> 
  お使いのOSは?
  <label>
    <input type="radio" name="os" value="windows" />Windows
  </label>
  <label>
    <input type="radio" name="os" value="mac" />Mac OS
  </label>
  <label>
    <input type="radio" name="os" value="unix" />Unix
  </label>
</div>
<input id="btn" type="button" value="送信" />
</form>
// 引数name:ラジオボタンの名前、value:設定値
let setRadioButton = function (name, value) {
  let elems = document.getElementsByName(name);
  // ラジオボタンを走査し、該当する値(value属性)を持つものをチェック
  for (let i = 0; i < elems.length; i++) {
    if (elems[i].value === value) {
      elems[i].checked = true;
    }
  }
};
setRadioButton('os','mac');

チェックボックスの設定

同じくチェックボックスの場合のコードは、以下です。

コードの大まかな流れは、ラジオボタンの場合とほぼ同じですが、複数選択できるようにsetCheckboxメソッドでも引数valueは配列で受け取ります。

<form>
  <div>
  お使いのOSは?
  <label>
    <input type="checkbox" name="os" value="windows" />Windows
  </label>
  <label>
    <input type="checkbox" name="os" value="mac" />Mac OS
  </label>
  <label>
    <input type="checkbox" name="os" value="unix" />Unix
  </label>
  </div>
  <input id="btn" type="button" value="送信" />
</form>
// 引数name:チェックボックスの名前、value:設定値(配列)
let setCheckbox = function (name, value) {
  let elems = document.getElementsByName(name);
  // ラジオボタンを走査し、該当する値(value属性)を持つものをチェック
  for (let i = 0; i < elems.length; i++) {
    if (value.indexOf(elems[i].value) > -1) {
      elems[i].checked = true;
    }
  }
};
setCheckbox('os',['windows','mac']);

複数選択できるリストボックスの値を取得/設定する

<option>要素のselectedプロパティを利用します。

<select>要素のvalueプロパティでは、選択された値の最初の1つしか取得できないので注意が必要です。

リストボックスの値を取得する

まずは、値取得のコードから見ていきます。

<form>
  <div>
    <label for="os">お使いのOSは?:</label>
    <select id="os" multiple size="3">
      <option value="windows">Windows
      <option value="mac">Mac OS
      <option value="unix">Unix
    </select>
  </div>
</form>
let getListbox = function (name) {
  // 選択値を格納するための配列を準備
  let result = [];
  // 指定されたリストボックス配下の<option>要素を取得
  let elems = document.getElementById(name).options;
  // <option>要素を順に走査し、選択状態にあるかを判定
  for (let i = 0; i < elems.length; i++) {
    if (elems[i].selected) {
      result.push(elems[i].value);
    }
  }
  return result;
};
// リストボックス変更時に、その値を取得
document.getElementById('os').addEventListener('click', function () {
  console.log(getListbox('os'));
}, false);

<option>要素は、<select>要素のoptionsプロパティで取得できます。(document.getElementById(name).options;

<option>要素を取得できたら、あとは、順にselectedプロパティのtruefalseを判定し、trueの場合に、そのvalueプロパティを結果配列に追加します。

リストボックスの値を設定する

リストボックスの値を設定する方法も同じ要領です。

<option>要素を順に走査し、設定値(配列value)にvalue属性と等しい値が含まれる場合に、そのselected属性をtrueにします。

<form>
  <div>
    <label for="os">お使いのOSは?:</label>
    <select id="os" multiple size="3">
      <option value="windows">Windows
      <option value="mac">Mac OS
      <option value="unix">Unix
    </select>
  </div>
</form>
// 引数name:リストボックスの名前、value:設定値(配列)
let setListbox = function (name, value) {
  // 指定されたリストボックス配下の<option>要素を取得
  let elems = document.getElementById(name).options;
  // <option>要素を順に走査し、選択状態にあるかを判定
  for (let i = 0; i < elems.length; i++) {
    if (value.indexOf(elems[i].value) > -1) {
      elems[i].selected = true;
    }
  }
};
setListbox('os',['windows','unix']);

ファイルの情報を参照する

<input type="file">要素のfilesプロパティを利用します。

<form>
  <div>
    <label for="file">ファイル:</label>
    <input id="file" name="file" type="file" multiple />
  </div>
</form>
let fl = document.getElementById('file');

// 選択されたファイルの情報をログに表示
document.getElementById('file').addEventListener('click', function (e) {
  for (let i = 0; i < fl.files.length; i++) {
    let input = fl.files[i];
    console.log('ファイル名:' + input.name);
    console.log('種類:' + input.type);
    console.log('サイズ:' + input.size / 1024 + 'KB');
    console.log('最終更新日時:' + new Date(input.lastModified));
  }
}, false);

filesプロパティは、指定されたファイル群をFileListオブジェクトとして返します。

そのため、「for (let i = 0; i < fl.files.length; i++) {...}」でもfor命令で順に個々のファイル(Fileオブジェクト)を取り出しています。

MEMO
ーmultiple属性ー
<input type="file">要素では、既定で1つのファイルしか指定できません。

複数ファイルを指定するには、明示的にmultiple属性を付与しなければならない点に注意が必要です。

ただし、multiple属性の有無に関わらず、filesプロパティの戻り値はFileListです。

あらかじめ単一指定であることがわかっている場合には、「~.files[0]」のように決め打ちで先頭要素を取得します。

Fileオブジェクトを取得できてしまえば、あとはそのプロパティから参照したい情報にアクセスするだけです。

Fileオブジェクトの主なメンバーは、次に掲げる表の通りです。

【Fileオブジェクトの主なメンバー】
プロパティ概要
nameファイル名
typeコンテンツタイプ
sizeサイズ(バイト単位)
lastModified最終更新日

テキストファイルを読み込む

ファイルの内容を読み込むには、FileReaderオブジェクトを利用します。

ただし、対象がテキストファイル、バイナリファイルいずれであるかによって、読み込みの方法が異なります。

まずは、テキストファイルを読み込む例です。

<form>
  <label for="file">ファイル:</label>
  <input id="file" name="file" type="file" />
  <input id="btn" type="button" value="表示" />
</form>
<hr />
<div id="result"></div>
let file = document.getElementById('file');
let result = document.getElementById('result');
let reader = new FileReader();
	
// ロード時にその読み取り結果をページに反映
reader.addEventListener('load', function () {
  result.innerHTML = reader.result.replace(/(\/n|\/r)/g, '<br />');
}, false);
	
// 読み取りに失敗した場合はエラーメッセージを表示
reader.addEventListener('error', function () {
  console.log('エラー発生:' + reader.error.message);
}, false);
	
// [表示]ボタンをクリックしたタイミングで、ファイルの内容を表示
document.getElementById('btn').addEventListener('click', function (e) {
  reader.readAsText(file.files[0], 'UTF-8');
}, false);

FileReaderを利用するにはまず、loadイベントリスナーを登録し、ファイルのロードに成功したときに実行すべき処理を定義します。(reader.addEventListener('load', function () {...}

ファイル内容にアクセスするには、resultプロパティを利用します。(reader.result.replace(/(\/n|\/r)/g, '<br />');

サンプルでは、replaceメソッドで改行文字を<br>要素に置き換えたうえで、ページに反映しています。

イベントリスナーの準備ができたら、あとはreadAsTextメソッドでファイルの内容をテキストとして読み込むだけです。

文字コードがUTF-8の場合は、引数encodingを省略することも可能です。(reader.readAsText(file.files[0], 'UTF-8');

readAsTextメソッド

reader.readAsText(file [,encoding])
file      読み込み対象のファイル
encoding  文字コード

reader.addEventListener('error', function(){...}は、FileReaderオブジェクトがファイルの読み込みに失敗した場合に実行されるコードです。

errorイベントリスナーの中では、error.messageプロパティでエラーメッセージを取得できます。

エラーを確認したい場合には、ファイルを選択したうえで[表示]ボタンをクリックする前に、そのファイルを削除して下さい。

結果、errorイベントリスナーが読み込まれて、以下のようなエラーメッセージがログ出力されます。

A requested file or directory could not be found at the time an operation was processed.

loaderrorの他にも、FileReaderでは、次に掲げる表のようなイベントが用意されています。

【FileReaderオブジェクトのイベント】
イベント発生タイミング
loadstart読み込みを開始した時
loadend読み込みを終了した時(成功/失敗に関わらない)
abort読み込みが中断されたとき
progressBlobコンテンツの読み込み時(読み込み中に連続して発生)

バイナリファイルを読み込む

readAsDataURLメソッドを利用します。

たとえば次のサンプルは、画像ファイルを読み込み、<img>要素に反映する例です。

<form>
  <label for="file">ファイル:</label>
  <input id="file" name="file" type="file" />
</form>
<hr />
<img id="result" />
let file = document.getElementById('file');
let reader = new FileReader();
	
// ロード時にその読み取り結果をページに反映
reader.addEventListener('load', function (e) {
  document.getElementById('result').src = reader.result;
}, false);
	
// ファイルを選択したタイミングで内容を表示
file.addEventListener('change', function(e){
  // 先頭のファイルだけを認識
  let input = file.files[0];
  // ファイルをバイナリ(Data URL形式)で読み込み
  reader.readAsDataURL(input);
}, false);

readAsDataURLメソッドは、ファイルの内容をData URL形式で取得します。(reader.readAsDataURL(input);

Data URLとは、URLにデータを直接埋め込むためのフォーマットで、次のような形式で表現できます。

DataURL形式とは

Data URL形式は、それ自体がデータなので、画像や音声などのデータをいちいちファイルにすることなく、srchrefなどの属性に埋め込めるというメリットがあります。

サンプルでも、resultプロパティ経由で取得したデータを、そのまま<img>要素のsrc属性にセットしています。(document.getElementById('result').src = reader.result;

ファイルをアップロードする

Fileオブジェクトを利用することで、指定されたファイルをサーバーにアップロードすることもできます。

<form>
  <input id="upfile" name="upfile" type="file" />
</form>
<div id="result"></div>
let result = document.getElementById('result');
let upfile = document.getElementById('upfile');
	
// ファイル選択時にアップロードを実行
upfile.addEventListener('change', function(e){
  // 先頭のファイルだけを認識
  let f = upfile.files[0];
  // アップロードファイルの準備
  let data = new FormData();
  data.append('upfile', f, f.name);
  // サーバーにデータを送信
  fetch('upload.php', {
    method: 'POST',
    body: data,
  })
  // 成功時にはアップロードしたファイル名を表示
  .then(function (response) {
    return response.text();
  })
  .then(function (text) {
    result.textContent = text;
  });
}, false);
<?php
$error = false;
if ($_FILES['upfile']['error'] !== UPLOAD_ERR_OK) { 
  $error = true;
} else {
  $src = $_FILES['upfile']['tmp_name']; 
  $dest = mb_convert_encoding($_FILES['upfile']['name'], 'SJIS-WIN', 'UTF-8');
  if (!move_uploaded_file($src, $dest)) { $error = true; }
}
if ($error) {
  header('HTTP/1.1 500 Internal Server Error');
  print 'アップロードできませんでした。';
} else {
  print 'アップロード成功:'.$dest;
}
?>

fileプロパティ経由で取得したFileオブジェクトは、そのままではアップロードできません。(=fetchメソッドに引き渡せません。)

そこでアップロードに適した形式に変換するのがFormDataオブジェクトの役割です。

multiple/form-data形式のフォームデータを、キー/値の形式で表現します。

FormDataにファイル(データ)を追加するのは、appendメソッドの役割です。(data.append('upfile', f, f.name);

appendメソッド

data.append(name, value [,file])
name    キー名
value   値
file    ファイル名

あとは、このFormDataオブジェクトをfetchメソッドのbodyパラメーターに引き渡すだけです。(fetch('upload.php', {...}

フォームへの入力値の妥当性を検証する

HTML/CSSの検証機能を利用します。

具体的には、次に掲げる表のような属性を利用することで、入力値を制限できます。

【入力値の制約に関わる属性】
属性検証内容
requiredなんらかの値が入力されているか
pattern正規表現にマッチしているか(<textarea>内では無効)
max最大値を超えていないか(type="number"のみ)
min最小値を下回っていないか(type="number"のみ)
maxlength文字列長が指定値より長くないか
minlength文字列長が指定値より短くないか
type="email"正しいメールアドレスか
type="url"正しいURL形式であるか

以上の属性を利用したフォーム例は、以下の通りです。

<h2>ユーザー登録</h2>
<form id="fm">
  <div>
    <label for="name">氏名:</label><br />
    <input id="name" type="text" name="name" minlength="2" maxlength="10" required>
  </div>
  <div>
    <label for="age">年齢:</label><br />
    <input id="age" type="number" name="age" min="20" max="60" />
  </div>
  <div>
    <label for="zip">郵便番号:</label><br />
    <input id="zip" type="text" name="zip" pattern="\d{3}-\d{4}" />
  </div>
  <div>
    <label for="mail">メールアドレス:</label><br />
    <input id="mail" type="email" name="mail" />
  </div>
  <div>
    <label for="url">URL:</label><br />
    <input id="url" type="url" name="url" />
  </div>
    <input id="send" type="submit" value="送信" />
</form>

検証エラーの見え方は、ブラウザーの種類/バージョンによって変化します。

MEMO
ークライアントサイド検証ー
クライアントサイド検証は、あくまでも一時的な検証にすぎません。

マークアップを改ざんしたり、JavaScriptを無効にすることで、比較的簡単に通過できてしまうからです。

サーバー側で処理する前に、最終的な検証を行うようにする必要があります。(現在よく利用されているフレームワークのほとんどは、検証機能を備えています。)

検証の成否に応じてスタイル切り替える

スタイルシートでは、検証の状態を判定するために、次に掲げる表のような疑似クラスを用意しています。

【検証関連の疑似クラス】
疑似クラス概要
:valid検証に成功した
:invalid検証に成功した
:in-rangemin~max属性の範囲内にある
:out-of-rangemin~max属性の範囲外にある
:requiredrequired属性が設定されている

たとえば、検証に失敗した場合にもに背景色をピンクにしたい場合、以下のようなスタイルシートを用意します。

<h2>ユーザー登録</h2>
<form id="fm">
  <div>
    <label for="name">氏名:</label><br />
    <input id="name" type="text" name="name" minlength="2" maxlength="10" required>
  </div>
  <div>
    <label for="age">年齢:</label><br />
    <input id="age" type="number" name="age" min="20" max="60" />
  </div>
  <div>
    <label for="zip">郵便番号:</label><br />
    <input id="zip" type="text" name="zip" pattern="\d{3}-\d{4}" />
  </div>
  <div>
    <label for="mail">メールアドレス:</label><br />
    <input id="mail" type="email" name="mail" />
  </div>
  <div>
    <label for="url">URL:</label><br />
    <input id="url" type="url" name="url" />
  </div>
    <input id="send" type="submit" value="送信" />
</form>
input:valid {
  background-color: rgb(255,255,255);
}
input:invalid {
  background-color: rgb(255,192,203);
}

検証メッセージをカスタマイズする

HTML標準で表示される検証メッセージは、ブラウザーのロケール(地域)設定に依存しており、要素/属性から変更することはできません。

ブラウザーのロケール設定とは別に言語を決めたい場合、そもそもアプリ独自のメッセージを表示したい場合には、JavaScriptの検証APIを利用します。

以下は[郵便番号]欄の入力値が正しくない場合(=pattern属性に違反している場合)のエラーメッセージを独自のものに置き換える例です。

<h2>ユーザー登録</h2>
<form id="fm">
  <div>
    <label for="name">氏名:</label><br />
    <input id="name" type="text" name="name" minlength="2" maxlength="10" required />
  </div>
  <div>
    <label for="age">年齢:</label><br />
    <input id="age" type="number" name="age" min="20" max="60" />
  </div>
  <div>
    <label for="zip">郵便番号:</label><br />
    <input id="zip" type="text" name="zip" pattern="\d{3}-\d{4}" />
  </div>
  <div>
    <label for="mail">メールアドレス:</label><br />
    <input id="mail" type="email" name="mail" />
  </div>
  <div>
    <label for="url">URL:</label><br />
    <input id="url" type="url" name="url" />
  </div>
    <input id="send" type="submit" value="送信" />
</form>
let fm = document.getElementById('fm');
let zip = document.getElementById('zip');

fm.addEventListener('input', function(e){
  if (zip.validity.patternMismatch) {
    zip.setCustomValidity('郵便番号の形式が間違っています。');
  } else {
    zip.setCustomValidity('');
  }
}, false);

validityプロパティは、フォーム要素の検証結果(状態)を管理するValidityStateオブジェクトを返します。

ValidityStateオブジェクトは、次に掲げる表のようなプロパティを提供します。

【ValidityStateオブジェクトの主なメンバー】
プロパティ概要
valid入力検証をすべて通過した場合にtrue
valueMissing必須入力で値がない場合にtrue
patternMismatch決められたパターンに一致しない場合にtrue
rangeOverflow上限(max属性)を上回る場合にtrue
rangeUnderflow下限(min属性)を下回る場合にtrue
tooLong文字列長が上限(maxlength属性)を超える場合にtrue
tooShort文字列長が下限(minlength)を下回る場合にtrue
typeMismatchtype属性で指定された形式にマッチしない場合にtrue
stepMismatchstep属性の幅に一致しない場合にtrue
customError独自のエラー状態にある場合にtrue

上記の例であれば、「if (zip.validity.patternMismatch) {」でテキストボックスzipがpattern制約に反しているかを確認し、反している場合にはカスタムのメッセージを、反していなければ空メッセージを、それぞれ設定しています。

フォーム要素にエラーメッセージを設定するのは、setCustomValidityメソッドの役割です。(zip.setCustomValidity('郵便番号の形式が間違っています。');

JavaScript独自のエラー検証を実装する

HTMLによる検証ではエラーメッセージの見え方も、ブラウザーの実装によって変化します。

また、検証が実施されるのはサブミット時です。

ブラウザー間でエラーの表示方法を統一したい、入力中でも 検証を実施したいなど、アプリで独自に検証機能を実装するならば、HTML標準の検証機能を無効にしたうえで、JavaScriptの検証APIを利用します。

たとえば以下は、HTML標準の検証機能を独自の検証機能で置き換える例です。

<h2>ユーザー登録</h2>
<form id="fm" novalidate>
  <div>
    <label for="name">氏名:</label><br />
    <input id="name" type="text" name="name" minlength="2" maxlength="10" required />
  </div>
  <div id="name_error" class="error"></div>
  <div>
  <label for="age">年齢:</label><br />
    <input id="age" type="number" name="age" min="20" max="60" />
    </div>
  <div>
    <label for="zip">郵便番号:</label><br />
    <input id="zip" type="text" name="zip" pattern="\d{3}-\d{4}" />
  </div>
  <div>
    <label for="mail">メールアドレス:</label><br />
    <input id="mail" type="email" name="mail" />
  </div>
  <div>
    <label for="url">URL:</label><br />
    <input id="url" type="url" name="url" />
  </div>
    <input id="send" type="submit" value="送信" />
</form>
let fm = document.getElementById('fm');
let name = document.getElementById('name');
let nameError = document.getElementById('name_error');

// [名前]欄の入力をチェック
function checkName () {
  if (name.validity.valueMissing) {
    nameError.innerHTML = '氏名が入力されていません。';
  } else {
    nameError.innerHTML = '';
  }
}
		
// サブミット/入力時に、入力値の検証を実施
fm.addEventListener ('submit', function (e) {
  checkName();
  e.preventDefault();
}, false);
	
fm.addEventListener('input', function (e) {
  checkName();
}, false);

独自の検証機能を実装する場合、HTML標準の検証機能とバッティングしないよう、HTML側のそれは無効化します。

そのためには<form>要素にnovalidte属性を付与します。(<form id="fm" novalidate>

あとは、validityプロパティでフォーム要素の状態をチェックし、その結果に応じて、エラーメッセージを反映します。(JavaScriptのコード7~11行目)

ここでは、検証関数(checkName)をinputsubmitイベントリスナーに割り当てているので、入力/サブミット時双方で検証処理を実行&エラーメッセージを反映します。(JavaScriptのコード15~22行目)


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です