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

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

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

まず最初はイベント登録用関数です。

イベント登録用関数

  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を文字列にしてみると,

IE以外のブラウザ

function(node,type,handler){
  node.addEventListener(type,handler,false);
}

IEの場合

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 = [];
    /* 続く */
  });

早速,上で定義したaddEventを使って,windowのloadイベントを待ちます。ロード後はその後使用する要素をdocument.getElementByIdで取得しておきます。使用する度にgetElementByIdを使うのではなく,予め取得してローカルの変数にしておけば,実際使用するときにローカル変数として高速にアクセスできます。フォーム内のinput要素は,フォーム要素からのgetElementsByTagName('input')で取得しています。フォーム要素を起点にすることで,そのフォーム内の要素だけを取得できます。また,フォーム要素は自分自身の持つ要素を保持しているので,getElementsByTagNameを使わなくてもinput要素を取得することは可能です。

フォームの走査#1

for (var i = 0, inputsLen = form.length;i < inputsLen; i++){
  var input = form[i];
}

フォームの走査#2

for (var i = 0, inputsLen = form.elements.length;i < inputsLen; i++){
  var input = form.elements[i];
}

ただし,この場合input要素以外の要素も含まれる点に注意が必要です。特に,Safari,Chromeではfieldset要素が含まれず,それ以外のブラウザではfieldsetも含まれるなどブラウザごとの細かな挙動の違いもあります。

続いて,テキストインプットにフォーカスが当たった際に背景色を白に戻す処理を実装しておきます。また,passwordは後で一致をチェックするのでここで配列に入れておきます。

テキストボックスのフォーカス処理

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関数は,handler関数内のthisがイベントを設定したノード自身になるように実装してあります。ここで,

ローカル変数の誤った使いかた

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は何度も書き換えられており,イベントが発生した時にinputが参照しているのはかならず最後のinputになってしまいます。

著者プロフィール

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

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

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