Skip to content

テクニック SCR32:クライアントサイドのバリデーションを提供し、DOM を介してエラーテキストを追加する

このテクニックについて

このテクニックは、次に関連する:

このテクニックは、HTML と併用されるスクリプトに適用される。

解説

このテクニックの目的は、クライアントサイドでフォームフィールドの検証に失敗したときにエラーメッセージを表示する方法について説明することである。アンカー要素はリスト中でエラーメッセージを表示させる際に使用され、検証が必要なフィールドの上に挿入される。フォーカスをエラーメッセージの場所に移し、利用者の注意を引くために、アンカー要素がエラーメッセージに使用される。アンカー要素の href は、エラーがみつかったフィールドへのページ内リンクを含む。

配置されたアプリケーションにおいて、もし JavaScript が無効になっていれば、クライアントサイドの検証は行われない。そのため、このテクニックはスクリプトが適合性において信頼できる、又はサーバーサイドの検証技術があらゆるエラーを発見し、エラーを含むフィールドの情報とともにページを返すように用いられている場合のみ、十分であるといえる。

事例

事例 1: フォーム冒頭のブロックにエラーを一覧表示する

この事例は必須のフィールドを検証し、さらに特定の書式が必要なフィールドを検証する。エラーがみつかったとき、スクリプトはエラーメッセージの一覧を DOM に挿入し、フォーカスをそこへ移動する。

正しく入力されなかったいくつかのフィールドのエラーメッセージを示すスクリーンショット
エラーメッセージは、フォームの上部にリンクのリストとして表示される。

フォームの HTML

<!DOCTYPE HTML>
<html lang="en">
  <head>
    <title>Form Validation</title>
    <meta charset="utf-8">
    <link href="css/validate.css" rel="stylesheet">
    <script src="scripts/validate.js">
  </head>
  <body>
    <h1>Form Validation</h1>
    <p>The following form is validated before being submitted if scripting
      is available, otherwise the form is validated on the server. All fields are
      required, except those marked optional. If errors are found in the submission,
      the form is cancelled and a list of errors is displayed
      at the top of the form.</p>

    <p>Please enter your details below.</p>
	
    <h2>Validating Form</h2>
    <form id="personalform">
      <div class="validationerrors"></div>
      <fieldset>
        <legend>Personal Details</legend>
          <div>
            <label for="forename">Please enter your forename</label>
            <input autocomplete="given-name" class="string"
             id="forename" name="forename" type="text" value="">
         </div>
         <div>
            <label for="age">Please enter your age</label>
            <input class="number" id="age" name="age" type="text" value="">
         </div>
         <div>
           <label for="email">Please enter your email address</label>
           <input autocomplete="email" class="email" id="email"
            name="email" type="text" value="">
         </div>
      </fieldset>
      <div>
        <input type="submit" name="signup" value="Sign up">
      </div>
    </form>
    
    <h2>Second Form</h2>
    <form id="secondform">
      <div class="validationerrors"></div>
      <fieldset>
        <legend>Second Form Details</legend>
          <div>
            <label for="suggestion">Enter a suggestion</label>
            <input class="string" id="suggestion" name="suggestion" type="text" value="">
          </div>
          <div>
            <label for="optemail">Please enter your email address (optional)</label>
            <input autocomplete="email" class="optional email" id="optemail"
             name="optemail" type="text" value="">
          </div>
          <div>
            <label for="rating">Please rate this suggestion</label>
            <input class="number" id="rating" name="rating" type="text" value="">
          </div>
          <div>
            <label for="jibberish">Enter some jibberish (optional)</label>
            <input id="jibberish" name="jibberish" type="text" value="">
          </div>
      </fieldset>
      <div>
        <input type="submit" name="submit" value="Add Suggestion">
      </div>
    </form>
  </body>
</html>

検証を行ってエラーメッセージを挿入する JavaScript

window.onload = initialise;
function initialise() {
  var objForms = document.getElementsByTagName('form');
  var iCounter;
	
  // Attach an event handler for each form
  for (iCounter=0; iCounter<objForms.length; iCounter++) {
    objForms[iCounter].onsubmit = function(){return validateForm(this);};
  }
}
		
// Event handler for the form
function validateForm(objForm) {
  var arClass = [];
  var iErrors = 0;
  var objField = objForm.getElementsByTagName('input');
  var objLabel = objForm.getElementsByTagName('label');
  var objList = document.createElement('ol');
  var objError, objExisting, objNew, objTitle, objParagraph, objAnchor, objPosition;
  var strLinkID, iFieldCounter, iClassCounter, iCounter;
	
  // Get the id or name of the form, to make a unique
  // fragment identifier
  if (objForm.id) {
    strLinkID = objForm.id + 'ErrorID';
  }
  else {
    strLinkID = objForm.name + 'ErrorID';
  }
	
  // Iterate through input form controls, looking for validation classes
  for (iFieldCounter=0; iFieldCounter<objField.length; iFieldCounter++) {
    
  // Get the class for the field, and look for the appropriate class
  arClass = objField[iFieldCounter].className.split(' ');
  for (iClassCounter=0; iClassCounter<arClass.length; iClassCounter++) {
    switch (arClass[iClassCounter]) {
      case 'string':
        if (!isString(objField[iFieldCounter].value, arClass)) {
          if (iErrors === 0) {
            logError(objField[iFieldCounter], objLabel, objList, strLinkID);
          }
          else {
            logError(objField[iFieldCounter], objLabel, objList, '');
          }
          iErrors++;
        }
      break;
      case 'number':
        if (!isNumber(objField[iFieldCounter].value, arClass)) {
          if (iErrors === 0) {
            logError(objField[iFieldCounter], objLabel, objList, strLinkID);
          }
          else {
            logError(objField[iFieldCounter], objLabel, objList, '');
          }
          iErrors++;
        }
      break;
      case 'email' :
        if (!isEmail(objField[iFieldCounter].value, arClass)) {
          if (iErrors === 0) {
            logError(objField[iFieldCounter], objLabel, objList, strLinkID);
          }
          else {
            logError(objField[iFieldCounter], objLabel, objList, '');
          }
          iErrors++;
        }
        break;
      }
    }
  }
	
  if (iErrors > 0) {

    // If not valid, display error messages
    objError = objForm.getElementsByTagName('div');
				
    // Look for existing errors
    for (iCounter=0; iCounter<objError.length; iCounter++) {
      if (objError[iCounter].className == 'validationerrors') {
        objExisting = objError[iCounter];
      }
    }
	
    objNew = document.createElement('div');
    objTitle = document.createElement('h2');
    objParagraph = document.createElement('p');
    objAnchor = document.createElement('a');
	
    if (iErrors == 1) {
      objAnchor.appendChild(document.createTextNode('1 Error in Submission'));
    }
    else {
      objAnchor.appendChild(document.createTextNode(iErrors + ' Errors in Submission'));
    }

    objAnchor.href = '#' + strLinkID;
    objAnchor.className = 'submissionerror';

    objTitle.appendChild(objAnchor);
    objParagraph.appendChild(document.createTextNode('Please review the following'));
    objNew.className = 'validationerrors';

    objNew.appendChild(objTitle);
    objNew.appendChild(objParagraph);
    objNew.appendChild(objList);
				
    // If there were existing error, replace them with the new lot,
    // otherwise add the new errors to the start of the form
    if (objExisting) {
      objExisting.parentNode.replaceChild(objNew, objExisting);
    }
    else {
      objPosition = objForm.firstChild;
      objForm.insertBefore(objNew, objPosition);
    }
	
    // Allow for latency
    setTimeout(function() { objAnchor.focus(); }, 50);
				
    // Don't submit the form
    objForm.submitAllowed = false;
    return false;
  }
	
  // Submit the form
  return true;
	}
	
  // Function to add a link in a list item that points to problematic field control
  function addError(objList, strError, strID, strErrorID) {
    
		var objListItem = document.createElement('li');
    var objAnchor = document.createElement('a');
		 
    // Fragment identifier to the form control
    objAnchor.href='#' + strID;

    // Make this the target for the error heading
    if (strErrorID.length > 0) {
      objAnchor.id = strErrorID;
    }
	
    // Use the label prompt for the error message
    objAnchor.appendChild(document.createTextNode(strError));

    // Add keyboard and mouse events to set focus to the form control
    objAnchor.onclick = function(event){return focusFormField(this, event);};
    objAnchor.onkeypress = function(event){return focusFormField(this, event);};
    objListItem.appendChild(objAnchor);
    objList.appendChild(objListItem);
  }

  function focusFormField(objAnchor, objEvent) {
    var strFormField, objForm;

    // Allow keyboard navigation over links
    if (objEvent && objEvent.type == 'keypress') {
      if (objEvent.keyCode != 13 && objEvent.keyCode != 32) {
        return true;
      }
    }
	
    // set focus to the form control
    strFormField = objAnchor.href.match(/[^#]\w*$/);
    objForm = getForm(strFormField);
    objForm[strFormField].focus();
    return false;
  }

    // Function to return the form element from a given form field name
    function getForm(strField) {
      var objElement = document.getElementById(strField);

    // Find the appropriate form
    do {
      objElement = objElement.parentNode;
    } while (!objElement.tagName.match(/form/i) && objElement.parentNode);

    return objElement;
  }
	
  // Function to log the error in a list
  function logError(objField, objLabel, objList, strErrorID) {
    var iCounter, strError;

    // Search the label for the error prompt
    for (iCounter=0; iCounter<objLabel.length; iCounter++) {
      if (objLabel[iCounter].htmlFor == objField.id) {
        strError = objLabel[iCounter].firstChild.nodeValue;
      }
    }

    addError(objList, strError, objField.id, strErrorID);
  }
	
  // Validation routines - add as required

  function isString(strValue, arClass) {
    var bValid = (typeof strValue == 'string' && strValue.replace(/^\s*|\s*$/g, '') 
    !== '' && isNaN(strValue));

    return checkOptional(bValid, strValue, arClass);
  }

  function isEmail(strValue, arClass) {
    var objRE = /^[\w-\.\']{1,}\@([\da-zA-Z\-]{1,}\.){1,}[\da-zA-Z\-]{2,}$/;
    var bValid = objRE.test(strValue);

    return checkOptional(bValid, strValue, arClass);
  }
	
  function isNumber(strValue, arClass) {
    var bValid = (!isNaN(strValue) && strValue.replace(/^\s*|\s*$/g, '') !== '');

    return checkOptional(bValid, strValue, arClass);
  }

  function checkOptional(bValid, strValue, arClass) {
    var bOptional = false;
    var iCounter;

    // Check if optional
    for (iCounter=0; iCounter<arClass.length; iCounter++) {
      if (arClass[iCounter] == 'optional') {
        bOptional = true;
      }
    }

    if (bOptional && strValue.replace(/^\s*|\s*$/g, '') === '') {
      return true;
    }

    return bValid;
  }

PHP、JavaScript、CSS 及び HTML を用いて実装されている、このテクニックの動作例: Form Validation Example.

検証

手順

アンカータグ及び上記のテクニックにある適切なスクリプトを用いて、エラーメッセージを作成する。

  1. ページを読み込む。
  2. エラーメッセージに関連付けられたフィールドに有効な値を入力し、エラーメッセージが表示されないことを確認する。
  3. エラーメッセージに関連付けられたフィールドに無効な値を入力し、そのフィールドに正確なエラーメッセージが表示されることを確認する。
  4. エラーメッセージがフォーカスを受け取ることを確認する。
  5. 表示されたエラーメッセージと関連付けられたフィールドに有効な値を入力し、エラーメッセージが除去されることを確認する。
  6. アンカータグによって作成されたエラーメッセージと関連付けられた全てのフィールドに対して、繰り返す。

注記

上記の手順を、支援技術を用いて実行することも推奨する。

期待される結果

  • チェック 2、3、4 及び 5 の全てが真である。
Back to Top