これでできる! クロスブラウザJavaScript入門

第1回 ウェブブラウザとJavaScriptの未来

この記事を読むのに必要な時間:およそ 10.5 分

ケーススタディ:入力値チェック#1

ここまででもかなりのボリュームとなってしまいましたが,ここからは実際にクロスブラウザなコードを実践して書いていきます。

今回は,最も使われるであろうフォームの入力値チェックです。まず,以下のようなよくあるユーザー登録フォームのHTMLを用意しました。

フォームのHTML

<form id="userform" name="userform" action="" method="post">
  <fieldset>
    <legend>ユーザー登録</legend>
    <p>
      <label for="username">ユーザー名</label>
      <input type="text" name="name" value="" id="username" class="text" required="required">
    </p>
    <p>
      <label for="useremail">メールアドレス</label>
      <input type="text" name="email" value="" id="useremail" class="text" required="required">
    </p>
    <p>
      <label for="userpass">パスワード</label>
      <input type="password" name="pass" value="" id="userpass" class="text" required="required">
    </p>
    <p>
      <label for="userpass_sub">パスワード(確認)</label>
      <input type="password" name="pass2" value="" class="text" id="userpass_sub">
    </p>
    <p class="submit">
      <input type="submit" id="usersubmit" value="送信" class="button" id="usersubmit">
    </p>
    <p id="errors"></p>
  </fieldset>
</form>

まず,必須項目のチェック,パスワードの一致のチェックという基本的な処理をjQueryを使って実装してみます。

送信ボタンを押した際に,各テキストボックスが空だったらその要素の背景色を黄色にして,メッセージを表示(3秒後に自動的に隠れる)します。パスワードは一致していなかった場合も背景色を黄色に変え,メッセージを表示します。最初にエラーになった要素に自動的にフォーカスを戻し,フォーカスが当たった要素は背景色を白に戻すようにします。

jQueryを使った実装

jQuery(function($){
  //htmlの構築を待つ
  var query = '#userform input:text,#userform input:password';
  var $inputs = $(query).focus(function(evt){
    $(this).css('background','white');
    // inputにフォーカスしたとき背景色を白に戻す
  }); // input要素はキャッシュしておく
  var message = {name:'ユーザー名',email:'メールアドレス',pass:'パスワード'};
  var $password = $('#userform input:password');
  $('#userform').submit(function(evt){ // 送信時の処理
    var isError = false, errors = [];
    var noBlank = ($inputs.filter(function(i,input){
      // requiredになっていて,値が入力されてないinputをフィルタリング
      return input.required && !input.value && errors.push(message[input.name]);
      // エラーメッセージ用に空だったinputに対応するメッセージを取得
    }).css('background','#ffff66').eq(0).focus().size() === 0);
    // フィルタされた要素について背景色を黄色にして1つ目の要素にフォーカスをあてる,
    // その時の要素数が0でなければ空のinputがあったとみなす
    var pass = $password.map(function(i,v){return v.value});
     // passwordの値を取得して配列に入れる
    var matchedPass = pass[0] === pass[1];
     // パスワードの一致を確認
    if (noBlank && !matchedPass) {
      // 入力漏れがなくて,パスワードが一致しない場合
      $password.css('background','#ffff66').eq(0).focus();
      $('#errors').text('パスワードが一致していません');
      isError = true;
    }
    if (errors.length) { // エラーメッセージが存在する場合
      $('#errors').text(errors.join(',')+'は必須です');
      isError = true;
    }
    if (isError) {
      $('#errors').show().delay(3000).hide('fast');
       //エラーメッセージを表示して,3秒後に隠す
    }
    return noBlank && matchedPass;
  });
});

やや強引ですが,なるべくコンパクトな(コメントを除いて28行ほど)実装にしました。もちろんこれは実装の一例であり,改善の余地は多くありますが,jQueryについてはこれ以上触れません。

続いて,同等の機能をjQueryを使わずに実装してみます。

ライブラリを使わない入力値チェック実装

(function(){
  var addEvent;
  if(document.addEventListener) {
    addEvent = function(node,type,handler){
      node.addEventListener(type,handler,false);
    };
  } else if (document.attachEvent) {
    addEvent = function(node,type,handler){
      node.attachEvent('on' + type, function(evt){
        handler.call(node, evt);
      });
    };
  } else {
    addEvent = function(node,type,handler){
      var _handler = node['on' + type];
      node['on' + type] = function(evt){
        if (_handler) {
          _handler.call(node, evt||window.evt);
        }
        handler.call(node, evt);
      };
    };
  }
  var message = {name:'ユーザー名',email:'メールアドレス',pass:'パスワード'};
  addEvent(window,'load',function(){
    var form = document.getElementById('userform');
    var errorNode = document.getElementById('errors');
    var inputNodes = form.getElementsByTagName('input');
    var passwords = [];
    var timer;
    for (var i = 0, inputsLen = inputNodes.length;i < inputsLen; i++){
      var input = inputNodes[i];
      if (input.type === 'text' || input.type === 'password') {
        addEvent(input,'focus',function(){
          this.style.background = 'white';
        });
        if (input.type === 'password') {
          passwords.push(input);
        }
      }
    }
    addEvent(form,'submit',function(evt){
      var noBlank = true;
      var errors = [];
      for (var i = 0;i < inputsLen; i++){
        var input = inputNodes[i];
        if (input.getAttribute('required') && !input.value) {
          input.style.background = '#ffff66';
          if(noBlank) {
            input.focus();
            noBlank = false;
          }
          errors.push(message[input.name]);
        }
      }
      var matchedPass = passwords[0].value === passwords[1].value;
      if (noBlank && !matchedPass) {
        passwords[0].focus();
        passwords[1].style.background = '#ffff66';
        errorNode.innerHTML = 'パスワードが一致していません';
      }
      if (!noBlank) {
        errorNode.innerHTML = errors.join(',')+'は必須です';
      }
      if (!noBlank || !matchedPass) {
        if (evt.preventDefault) {
          evt.preventDefault();
        } else {
          evt.returnValue = false;
        }
        errorNode.style.display = 'block';
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        timer = setTimeout(function(){
          errorNode.style.display = 'none';
          timer = null;
        }, 3000);
      }
    });
  })
})();

一気に80行ほどのコードになってしまいました。こちらを順に解説していきます。

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/