ライブラリを使わないでDOMにアクセスするJavaScriptを書いたときにはまった2つのこと

ライブラリを使わないでDOMにアクセスするJavaScriptを書いたときにはまった2つのこと

タスク管理するのに手帳を使おうとして2週間で挫折したので、iPhone & Mac でできないか挑戦している tanaka です。

最近、諸事情により jQuery などのライブラリ を使わずに DOM を操作する JavaScript コードを書く機会がありました。ある程度はライブラリなしで書いたこともあるし、そんな大変じゃないだろう…と思っていたら、想定外に時間がかかってしまいました。(多くはレガシーなIE対応ですが…)そんなわけで、ライブラリを使わないで JavaScript を書いたときに遭遇した問題2つを紹介します。サンプルコードはQUnitでテストを書き、テスト実行+コードリーディングすることでIEとそれ以外のブラウザの違いが理解できるようにしました。

IE 7以下で setAttribute('class', [classNames])/getAttribute('class')が機能しない

test("setAttribute('class') test", function() {
    var p = document.getElementById('attr-test');
    p.setAttribute('class', 'test');
    ok('test' === p.className, 'Passed!?');
});

こちらのコードは QUnit を使って機能を検証するため書いたコードです。IE6/IE7では失敗します。IEでclass属性を操作するにはsetAttribute('className', [classNames]) としなければいけないようです。テストコードでも書いてますが、単なる className プロパティであればだいたいのモダンブラウザで使えるので、そちらをつかったほうがいいようです。

イベントハンドラ内でthisの値がIEとそれ以外で異なる

IE検証用コード

test("IE's 'this' value in event handler", function() {
    var a = document.getElementById('event-test'),
            eventHandlerThis;
    a.attachEvent('onclick', function() {
        eventHandlerThis = this;
    });
    a.fireEvent('onclick');
    ok(window === eventHandlerThis, 'Passed!?');
});

IE以外検証用コード

test("Not IE's 'this' value in event handler", function() {
    var a = document.getElementById('event-test2'),
            eventHandlerThis;

    a.addEventListener('click', function() {
        eventHandlerThis = this;
    });

    var clickEvent = window.document.createEvent("MouseEvent");
    clickEvent.initEvent("click", false, true);
    a.dispatchEvent(clickEvent);
    ok(a === eventHandlerThis, 'Passed!?');
});

ボタンがクリックされたときにその要素を取得しようとしたらIE以外では要素オブジェクトが取得できましたが、IE で attachEvent を使ってイベントハンドラを登録した場合 関数内では this === window となりました。この挙動の差については、イベントハンドラ内で this を使わず、イベントオブジェクトから取得するのがよいようです。IE9 ではaddEventListenerが実装されていますが、そのときはその他のブラウザと同じ挙動になりました。

    // IEで attachEvent を使うとき
    a.attachEvent('onclick', function(e) {
        targetElement = e.target || e.srcElement;
    });
    // IE以外
    a.addEventListener('click', function(e) {
        targetElement = e.target || e.srcElement;
    });

上のコードはIE/それ以外の場合分けで書いてますが、イベントハンドラ関数の中は2つともまったく同じで、両方に対応できているコードになります。

付録・QUnitを使った機能テスト

上記のページに各ブラウザでアクセスすることで、setAttribute('class', [classNames])とイベントハンドラ内thisについてテストできます。2つ目と3つ目のテストはほとんどのブラウザでどちらかが失敗しますが、IE9ではattachEventとaddEventListenerの両方が実装されていて、両方ともテスト成功しました。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ