WCAG 2.0 実装方法集

メインコンテンツへスキップ

-

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

適用(対象)

HTML又はXHTMLで使用されるスクリプト。

これは、次の達成基準に関連する実装方法である:

解説

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

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

事例

事例 1

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

サンプルの画面イメージ:スクリーンショットは、正しく記入が行われていないいくつかのフィールドのエラーメッセージをあらわしている。エラーメッセージはリンクリストでフォームの先頭近くに現れる。

HTML及びJavascriptのコード

これは事例のフォームのHTMLである:

コード例:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>    
        <title>フォームの検証</title>
        <link href="css/validate.css" rel="stylesheet" type="text/css"/>
        <script type="text/javascript" src="scripts/validate.js"/>
    </head>
    <body>

        <h1>フォームの検証</h1>

        <p>以下のフォームは、もしスクリプトが利用可能であれば送信前に検証されるが、
		そうでなければサーバー上で検証される。任意と書かれた以外のすべての
		フィールドは必須である。送信時にエラーがみつかった場合、フォームはキャンセルされて
		エラー一覧がフォームの先頭に表示される。</p>

        <p> 下に貴方の情報を入力してください。 </p>

        <h2>フォームの検証</h2>

        <form id="personalform" method="post" action="index.php">
            <div class="validationerrors"/>
            <fieldset>
                <legend>個人情報詳細</legend>
                <p>
                    <label for="forename">名前を入力してください</label>
                    <input type="text" size="20" name="forename" id="forename" class="string"
                        value=""/>
                </p>
                <p>
                    <label for="age">年齢を入力してください</label>
                    <input type="text" size="20" name="age" id="age" class="number" value=""/>
                </p>
                <p>
                    <label for="email">電子メールアドレスを入力してください</label>
                    <input type="text" size="20" name="email" id="email" class="email" value=""/>
                </p>
            </fieldset>
            <p>
                <input type="submit" name="signup" value="登録"/>
            </p>
        </form>
        <h2>2番目のフォーム</h2>
        <form id="secondform" method="post" action="index.php#focuspoint">
            <div class="validationerrors"/>
            <fieldset>
                <legend>2番目のフォームの詳細</legend>
                <p>
                    <label for="suggestion">提案内容を入力</label>
                    <input type="text" size="20" name="suggestion" id="suggestion" 
                      class="string" value=""/>
                </p>
                <p>
                    <label for="optemail">貴方のメールアドレスを入力してください(任意)</label>
                    <input type="text" size="20" name="optemail" id="optemail"
                        class="optional email" value=""/>
                </p>
                <p>
                    <label for="rating">この提案を評価してください</label>
                    <input type="text" size="20" name="rating" id="rating" 
                      class="number" value=""/>
                </p>
                <p>
                    <label for="jibberish">合言葉を入力してください(任意)</label>
                    <input type="text" size="20" name="jibberish" id="jibberish" value=""/>
                </p>

            </fieldset>
            <p>
                <input type="submit" name="submit" value="提案の追加"/>
            </p>
        </form>
    </body>
</html>                      

以下は検証を行ってエラーメッセージを挿入するJavaScriptである:

コード例:


window.onload = initialise;

function initialise()
{
   var objForms = document.getElementsByTagName('form');
   var iCounter;

   // フォームそれぞれにイベント・ハンドラを追加
   for (iCounter=0; iCounter<objForms.length; iCounter++)
   {
      objForms[iCounter].onsubmit = function(){return validateForm(this);};
   }
}


// フォームのイベント・ハンドラ
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;

   // 部分識別子を固有にするため、
   // フォームのid又はnameを取得する
   if (objForm.id)
   {
      strLinkID = objForm.id + 'ErrorID';
   }
   else
   {
      strLinkID = objForm.name + 'ErrorID';
   }

   // validationクラスを探索するため、inputフォーム・コントロールをループする
   for (iFieldCounter=0; iFieldCounter<objField.length; iFieldCounter++)
   {
      // フィールドのクラスを取得し、適切なクラスを探す
      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)
   {
      // validではない場合、エラーメッセージを表示する
      objError = objForm.getElementsByTagName('div');
      
      // 存在しているエラーを探す
      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つあります'));
      }
      else
      {
         objAnchor.appendChild(document.createTextNode('送信した中にエラーが' + iErrors + '個あります'));
      }
      objAnchor.href = '#' + strLinkID;
      objAnchor.className = 'submissionerror';

      objTitle.appendChild(objAnchor);
      objParagraph.appendChild(document.createTextNode('以下をご確認ください'));

      objNew.className = 'validationerrors';

      objNew.appendChild(objTitle);
      objNew.appendChild(objParagraph);
      objNew.appendChild(objList);
      
      // 既にエラーがある場合、新しいエラーと交換する。
      // それ以外の場合、フォームの先頭に新しいエラーを追加する。
      if (objExisting)
      {
         objExisting.parentNode.replaceChild(objNew, objExisting);
      }
      else
      {
         objPosition = objForm.firstChild;
         objForm.insertBefore(objNew, objPosition);
      }

      // 待ち時間の設定
      setTimeout(function() { objAnchor.focus(); }, 50);
      
      // フォームを送信しない
      objForm.submitAllowed = false;
      return false;
   }

   // フォームを送信
   return true;
}

// 問題のあるフィールドコントロールを指すリスト項目にリンクを追加する関数
function addError(objList, strError, strID, strErrorID)
{
   var objListItem = document.createElement('li');
   var objAnchor = document.createElement('a');
   
   // フォーム・コントロールへの部分識別子
   objAnchor.href='#' + strID;

   // エラーの見出しに向けたターゲットにする
   if (strErrorID.length > 0)
   {
      objAnchor.id = strErrorID;
   }

   // エラーメッセージ用のラベルプロンプトを使う
   objAnchor.appendChild(document.createTextNode(strError));
   // フォーム・コントロールにフォーカスを当てるために、キーボード及びマウスイベントを追加する
   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;

   // キーボードでもリンクが機能するようにする
   if (objEvent && objEvent.type == 'keypress')
   {
      if (objEvent.keyCode != 13 && objEvent.keyCode != 32)
      {
         return true;
      }
   }

   // フォーム・コントロールにフォーカスを当てる
   strFormField = objAnchor.href.match(/[^#]\w*$/);
   objForm = getForm(strFormField);
   objForm[strFormField].focus();
   return false;
}

// 与えられたフォームフィールドの名前から、フォーム要素を返す関数
function getForm(strField)
{
   var objElement = document.getElementById(strField);

   // 適切なフォームを探す
   do
   {
      objElement = objElement.parentNode;
   } while (!objElement.tagName.match(/form/i) && objElement.parentNode);

   return objElement;
}

// リスト中のエラーを記録する関数
function logError(objField, objLabel, objList, strErrorID)
{
   var iCounter, strError;

   // エラープロンプトのラベルを探す
   for (iCounter=0; iCounter<objLabel.length; iCounter++)
   {
      if (objLabel[iCounter].htmlFor == objField.id)
      {
         strError = objLabel[iCounter].firstChild.nodeValue;
      }
   }

   addError(objList, strError, objField.id, strErrorID);
}

// 検証ルーティン - 要求事項として

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;

   // 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及びXHTMLで実装されている:フォーム検証の例

検証

チェックポイント

アンカータグと上記の実装方法による適切なスクリプトを用いて、エラーメッセージを作成する。

  1. ページを読み込む。

  2. エラーメッセージと結びつけられたフィールドに適切な値を入力し、エラーメッセージが表示されない。

  3. エラーメッセージと結びつけられたフィールドに不適切な値を入力し、そのフィールドに適したエラーメッセージが表示される。

  4. エラーメッセージにフォーカスが当たる。

  5. 表示されたエラーメッセージと関連するフィールドに適切な値を入力し、エラーメッセージがなくなる。

  6. アンカータグによってエラーメッセージと関連付けられたすべてのフィールドに対して、繰り返す。

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

判定基準

注意: この実装方法が「達成基準を満たすことのできる実装方法」の一つである場合、このチェックポイントや判定基準を満たしていなければ、それはこの実装方法が正しく用いられていないことを意味するが、必ずしも達成基準を満たしていないことにはならない。場合によっては、別の実装方法によってその達成基準が満たされていることもありうる。

日本語訳における注記:

この文書の正式版は、W3Cサイトで公開されている英語の文書であり、この日本語訳には誤訳が含まれていることもありえます。なお、文中にある「日本語訳における注記」は、W3Cの原文にはないものであり、日本語訳監修者が追記したものです。