最近javascriptでDOMをクロスブラウザで触ってみたまとめ


最近javascriptを使ってクロスブラウザでDOM操作とかしてみたので,それで分かったことや残したいことを今のうちにまとめておきます.
Ajaxについても書こうとしたけど時間やばくなってきたので次回まとめる.


なお自分で発見したことはほとんどなく,ほぼWeb上のリソースを参考にして書きますが,あまりに多いので割愛します.申し訳ありません.
Webすごい!すばらしい!!

■DOM要素のプロパティのブラウザによる違い

DOM要素のプロパティはブラウザによってかなり違います.
だからクロスブラウザjavascriptをがしがし動かそうとするとすごく大変.
なのでちょっとコツみたいなものをまとめます.
※使用したブラウザはIE8・Firefox3.6・Google Chrome8 dev版


  • 要素のプロパティを調べるときはブラウザの開発者ツールを使う

私は「どの要素がどういうプロパティを持つか」が知りたかったので,Webでそういうのがまとめてあるサイトを探し回ったのですが,あまりありませんでした.


なので実際にjavascriptを書いてブラウザの開発者ツールでデバッグしてプロパティ一覧を見た方が早いなぁと感じました.


最近ではIEは「開発者ツール」,Google Chromeでは「デベロッパーツール」がデフォルトでついてますし,Firefoxでは有名なプラグインFireBug」を使えばjavascirptのデバッグが行えます.


  • プロパティの名前の違いまとめ

イベントオブジェクトのイベントが発生した要素
IE:event.srcElement
IE以外:event.target


最初の子要素
IE:firstChild
IE以外:firstElementChild


兄弟要素
IE:nextSibling
IE以外:nextElementSibling
※特にIEではタグ内のテキスト要素?も兄弟要素とみなすっぽいです.

<div id="text_div">テキストだよ</div>
<ul></ul>

上記の場合,UL要素を取得する場合,
IE:document.getElementById('text_div').nextSibling.nextSibling
IE以外:document.getElementById('text_div').nextElementSibling
で取得できます.


タグ内のテキスト
IE:innerText
IE以外:textContent


他にもそれはそれはたくさんあります.
ちなみにChromeだとIE用に書いたコードでも大抵実行できました.Chromeすごい.
どうもmozillaIEが仲悪いみたい.



■イベントリスナの付加

タグ内に属性としてonclick="関数名"とやると,一つしか関数が実行できないのを防ぐよくある手段の一つ,イベントリスナ関数.
自分はWebにあったクロスブラウザ対応版にgetElementsByNameで取ってきた要素配列(NodeList)と選択ボックスにイベントリスナを付加できる誰得版に改変しました.

function addEvent(get_obj, evType, fn)
{
    // flagとかおまけ
    var flag = false;
    var length = 0;
    var target_objs = new Array();
    // getElementsByNameで取得あるいは選択ボックスの場合は配列なのでこちら
    if (get_obj.length) {
        // 選択ボックス
        if (get_obj.type == 'select-one') {
            target_objs[0] = get_obj;
            length = 1;
        }
        // NodeList
        else {
            target_objs = get_obj;
            length = target_objs.length;
        }
    }
    // getElementByIdで取得した場合は配列ではないのでこちら
    else {
        target_objs[0] = get_obj;
        length = 1;
    }

    for (var i = 0; i < length; i++) {
        if (target_objs[i].addEventListener){
            target_objs[i].addEventListener(evType, fn, false);
            flag = true;
        }
        else if (target_objs[i].attachEvent){
            target_objs[i].attachEvent('on'+evType, fn);
            flag = true;
        }
        else {
            flag = false;
        }
    }
    return flag;
}

getElementByIdで要素単体を取ってきた場合と,getElementsByNameでNodeListとして取ってきた場合と,選択ボックスをうまく判別したかったけどこんな泥臭い実装に・・・どうにかならないかなぁ.



■キー入力の5000ms後もしくは winではctrl+s,macではcommand+sを押すと関数実行

面倒なのでぺたし
PHP SPOTさん参考
http://phpspot.org/blog/archives/2006/12/javascript_35.html

※timer_idはグローバル変数で宣言しておく

function executeFunctionForShortcutKey(event)
{
    var ctrl, keycode, keychar;
    // Mozilla(Firefox, NN) and Opera
    if (event != null) {
        // Macの場合はcommandをctrlとみなす
        if (navigator.userAgent.indexOf("Mac") != -1) {
            ctrl = typeof event.modifiers == 'undefined' ? event.metaKey : event.modifiers & Event.META_MASK;
            keycode = event.which;
        }
        // WinかつIE
        else if (isIE) {
            keycode = event.keyCode;
            ctrl = event.ctrlKey;
        }
        // WinのIE以外のブラウザやLinuxなどの場合はctrl + s
        else {
            ctrl = typeof event.modifiers == 'undefined' ? event.ctrlKey : event.modifiers & Event.CONTROL_MASK;
            keycode = event.which;
        }
    }

    // キーコードの文字を大文字に統一して取得
    keychar = String.fromCharCode(keycode).toUpperCase();

    // Ctrl同時押しの場合
    if (ctrl) {
        if (keychar == "S") {
            // イベントの上位伝播を防止
            // Firefox対応 FirefoxはreturnValueが存在しない
            if (!('returnValue' in event)) {
                event.preventDefault();
            }
            else {
                event.returnValue = false;
            }
            event.cancelBubble = true;
            // ここで関数実行
            alert('関数実行はここだ!');
            
        }
    }
    else {
        // timer_idが未定義,もしくは初期化されていたら1500ms後に保存処理を実行するタイマーを設定
        if(!("timer_id" in window) || timer_id == 0) {
            timer_id = setTimeout(function(){
                           // ここで関数実行
                           alert('関数実行はここだ!');
                           timer_id = 0;
                           }, 
                       5000);
            reserved_time = new Date();
        }
        else {
            current_time = new Date();
            // 1500ms以下の時間で連続的にタイプされたら保存処理を止め,新たにタイマーを走らせる
            if (current_time - reserved_time < 5000) {
                clearTimeout(timer_id);
                // 新たにタイマーを走らせる
                timer_id = setTimeout(function(){
                               // ここで関数実行
                               alert('関数実行はここだ!');
                               timer_id = 0;}, 
                           5000);
                reserved_time = new Date();
            }
        }
    }
}

いじょ.

  • まとめ

Google Chrome最強伝説