こんにちは、
JavaScriptのイマ
JavaScriptは2010年現在において、
このクライアント側での処理について、
しかし、
主なJavaScriptエンジンには、
さらに、
と、
ウェブブラウザの種類とクセ
前置きが思いのほか長くなってしましたが、
さて、
まずは、
 
どのブラウザもECMA-262 3rd editionとDOM Level 1までは概ね足並みが揃っています
では、
IE(Internet Explorer) 
シェアNo.
- IE 5(もしくはそれ未満) 
- サポートの必要性はまずありません。 
- IE 5.5 
- 特に理由がなければ、 - サポートする必要はありません。もしもサポートする場合、 - CSSが後方互換モードしかない点や、 - JavaScriptではArray. - prototype. - pushをサポートしていないなど、 - 問題は多く、 - サポートには大きなコストがかかりますし、 - 一方でそれに見合うメリットがあることは滅多にないでしょう。メジャーなJavaScriptライブラリも基本的にIE5. - 5をサポートしていません。 
- IE 6
- 最も悩ましいバージョンです。IE 6をサポートしないという選択をすれば、 - サイト構築・ - 運営にかかるコストを抑えられるという面は確かにあるでしょう。ただし、 - IE 6に対応するためのノウハウ・ - バグは、 - これまで頻繁に話題になってきたので、 - 少し調べれば情報が出てくるという特徴もあります - (マイナーなブラウザでは自力で解決するしかないことが多いことに比べれば、 - 利点と言えなくもないでしょう)。例えば、 - position:fixedができない、 - 透過PNGを表示できないといった問題はそのまま検索すればすぐに対応方法が見つかります。jQueryやPrototype. - js、 - YUIなどメジャーなライブラリもIE 6をサポートしています。 - ただ、 - 最近ではGoogleが積極的にIE 6のサポートを終了するなど、 - IE 6に対応しないという風潮も出てきています。例えば、 - 新しいモノ好きな人向けのサイトでIE 6を積極的にサポートする必要性は弱くなるでしょうから、 - 場合によってもIE 6を基本機能しかサポートしないという選択も考えられます。 
- IE 7
- IE 6をサポートした時点で、 - IE 7もサポートできていることが多いので、 - 確認は後回しにしてしまっても問題ないバージョンです。逆に言えば、 - IE 7をサポートした時点で、 - IE 6のサポートはあと少しであることが多くなります。 
- IE 8
- CSSの実装はかなり優秀です。Firefox、 - Webkitをターゲットに書いたCSS - (もちろんCSS2. - 1の範囲ですが) - をIE 8ではそのまま同様に表示できることも珍しくありません。JavaScriptについては主にDOMのイベント処理周りが依然として独自実装ですが、 - やはりIE 6で動けばIE 8でも動くので特別な対応をする必要はほとんどないでしょう。 
Firefox
着実にシェアを伸ばしているFirefoxは、
- Firefox 1.5 (もしくはそれ未満) 
- サポートの必要性はまずありません。 
- Firefox 2
- Firefoxを提供しているMozilla自体がFirefox 2のサポートを終了しているため、 - サポートしなければいけない理由はありません。しかし、 - 現実にはFirefox 2を使い続けるユーザーは存在します。IE 5. - 5などに比べればFirefox 2のサポートは難しくないので、 - 基本機能が動作しているか確認するレベルでも問題ないでしょう。 - なお、 - CSSについては、 - display:inline-blockを使用する際に注意が必要だったりと、 - 少々問題が出ることもあります。JavaScriptについてはECMA-262 3rd editionベースであれば、 - 問題が出ることは少ないでしょう。 
- Firefox 3.0 
- HTML/ - CSS/ - JavaScriptを安定してサポートした模範的なウェブブラウザといえます。それゆえにFirefox 3. - 0での表示、 - 動作は1つの指針となります - (ただし、 - 前述の通りJavaScriptについては独自実装が多いので注意が必要です)。 
- Firefox 3.5、 3. 6 
- 基本的にはFirefox 3. - 0と同様です。ただし、 - JavaScriptエンジンにJITが採用され大幅な高速化が行われており、 - 一部の処理ではIE 6などの古いブラウザと大きな差があるため、 - Firefox 3. - 5基準の - (IEから見たら) - 重いJavaScriptを書かないように注意する必要があります。 
Safari
Firefoxに並んで、
- Safari 1.3、 2. 0 
- 特に理由がなければ、 - サポートしていなくても問題ないでしょう。JavaScriptを使用しないサイトであればそれなりに表示できるはずですが、 - Safari 1. - 3、 - 2. - 0でのJavaScript周りをサポートするのは情報が少ないため容易ではありません。主要なJavaScriptライブラリでは、 - prototype. - jsのみがSafari 2. - 0をサポートしているといった状況です。基本的に自力でサポートする必要があり、 - そのコストに見合うだけのメリットがあるかは疑問です。もしサポートする必要がある場合は、 - Safari の JavaScript の不備などが参考になります。 
- Safari 3.0.4 
- Safariのなかで、 - 最もやっかいなバージョンです。CSS周りでの問題は少ないですが、 - JavaScript関連では、 - 例えばaddEventListenerをサポートしているのに、 - DOMContentLoadedをサポートしていないなど、 - 少々癖のあるバージョンです。とはいえ、 - 主要なライブラリはSafari3. - 0からのサポートなので、 - ライブラリに頼れる範囲では難しくないでしょう。シェアとしては小さいので、 - サポートしないという選択も考えられます。 
- Safari 3.1、 3. 2 
- Safariの中では比較的安定したバージョンです。DOM/ - CSSのサポートはそこそこ、 - JavaScriptの処理速度などもそれなりに高速です。3. - 0.4に対応した後に、 - 確認する程度でも十分かもしれません。 
- Safari 4.0.4 
- 2010年3月時点での最新版です。DOM/ - CSSのサポートは十分なので、 - 標準仕様に合わせて実装すれば自然とサポートできるはずです。 
Google Chrome
レンダリングエンジンはSafariと同じWebKitを使用し、
Opera
日本でのシェアは今一つですが、
- Opera 9.2x (もしくはそれ未満) 
- すでにほとんど使われていないので、 - 特にサポートする必要はありません。 
- Opera 9.5、 9. 6、 10. x 
- DOM/ - CSSのサポートはそこそこで、 - IEやFirefoxとの互換性にも配慮されているため、 - 一般的なサイトをなるべく標準的な仕様に従って作成すれば、 - Operaでも正常に動作することがほとんどです。ただし、 - ところどころでクセが強いため、 - 右クリックのイベントが取れなかったり - (ユーザーが明示的に許可した場合のみ可能) - とイベント周りで特殊な動作をすることが多くなっています - (仕様に対して忠実に実装した結果であることが多く、 - Operaのバグというわけでもないといったこともあります)。また、 - 何か問題があった際は参考となる情報を見つけることが難しく、 - 自力で解決しなければいけないことが多くなっています。 
- Opera 10.50 
- 2010年3月にリリースされたOpera 10. - 50はJavaScriptの実行速度が大幅に高速化されており、 - HTML5などのサポートも大幅に進んでいます。基本的にはそれ以前のバージョンと変わりありません。 
以上、
ケーススタディ:入力値チェック#1
ここまででもかなりのボリュームとなってしまいましたが、
今回は、
<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(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;
  });
});やや強引ですが、
続いて、
(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行ほどのコードになってしまいました。こちらを順に解説していきます。
まず最初はイベント登録用関数です。
  var addEvent;//変数を宣言
  if(document.addEventListener) {
    //addEventListenerが使える場合(IE以外)
    addEvent = function(node,type,handler){
      node.addEventListener(type,handler,false);
    };
  } else if (document.attachEvent) {// IE用
    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.event);
        }
        handler.call(node, evt);
      };
    };
  }このaddEventを文字列にしてみると、
function(node,type,handler){
  node.addEventListener(type,handler,false);
}function(node,type,handler){
  node.attachEvent('on' + type, function(evt){
    handler.call(node, evt);
  });
}となっており、
  addEvent(window,'load',function(){
    var form = document.getElementById('userform');
    var errorNode = document.getElementById('errors');
    var inputNodes = form.getElementsByTagName('input');
    var passwords = [];
    /* 続く */
  });早速、
for (var i = 0, inputsLen = form.length;i < inputsLen; i++){
  var input = form[i];
}for (var i = 0, inputsLen = form.elements.length;i < inputsLen; i++){
  var input = form.elements[i];
}ただし、
続いて、
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関数を使います。addEvent関数は、
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(){
      input.style.background = 'white';
    });
  }
}のようにしてはいけません。forループの中でinputは何度も書き換えられており、
続いて、
addEvent(form,'submit',function(evt){
  var noBlank = true;
  var errors = [];
  /* 中略 */
  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);
  }
});エラーメッセージを表示し
では、
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]);
  }
}input.
先ほど配列に入れておいたpasswordの値が一致しているかチェックします。
var matchedPass = passwords[0].value === passwords[1].value;
if (noBlank && !matchedPass) {
  passwords[0].focus();
  passwords[1].style.background = '#ffff66';
  errorNode.innerHTML = 'パスワードが一致していません';
  isError = true;
}
if (!noBlank) {
  errorNode.innerHTML = errors.join('、')+'は必須です';
}空の要素がある場合はそちらのメッセージを優先しています。errorNodeにinnerHTMLでメッセージを挿入しています。テキストの書き換えなので、
  var textNode = document.createTextNode('パスワードが一致していません');
  if (errorNode.firstChild) {
    errorNode.replaceChild(textNode, errorNode.firstChild);
  } else {
    errorNode.appendChild(textNode);
  }少々長くなりましたが、
まとめ
初回限定の増量キャンペーンでお送りしましたがいかがでしたでしょうか? 次回からはもう少しライトな内容にシフトする予定です。
なお、
