jQuery初心者が感じた「便利!」と「こうした方がいいかも」と「孔明の罠」

現在ガシガシとjavascriptを書きまくっているのですが,jQueryを知ってからやっとjavascriptを書くことが楽しいと感じるようになりました.
そこでjQuery歴1ヶ月のヒヨっ子が「jQueryって便利だなぁっ!」または「jQueryのここは落とし穴だったわー」と感じたことをまとめてみたいと思います.
間違い or 勘違いを含んでいる可能性が大いにありますのでそのあたりはご了承下さい...



■便利!!セレクタ全般

まず外せないのはドル関数と呼ばれる$()またはjQuery()の引数として使う要素を取得する記法,セレクタです.
基本的なことは置いといて(id属性を持つ要素の取得は$('#id_name')だとか),ちょっと困ったのがname属性を持つ要素一覧の取得方法です.

正しいかどうか分かりませんが,自分はこうしてます.

// name属性がradio_nameであるラジオボタン要素一覧
// 'tag名[name="name属性"]'
$('input[name="radio_name"]')

ラジオボタンの例を出しましたので,その中でチェックが入っている要素の値を取得する方法です.

// ラジオボタン一覧の中でチェックされている要素のvalue
$('input[name="radio_name"]:checked').val();

ちなみにセレクトボックスから選択されているもののvalueを取得する場合は

// selectboxは単品である場合が多いのでid属性で取得
$('#id_name option:selected').val();

チェックボックスの場合は

// これだけで配列として返る
$('input[name="checkbox_name" :checked').map(function(){
    return $(this).val();
}).get();

です.



jQueryオブジェクトは頭文字に$をつける!

jQueryオブジェクトと普通のDOMが混ざったコードだと視認性が著しく低下する恐れがあるため,その変数がjQueryオブジェクトなのか普通のDOMなのか明確にするために変数名に$を付けると分かりやすい.



■普通のDOMでもjQueryメソッドを使いたい!

普通のDOMでも一瞬だけjQueryのメソッドを使いたい場合はjQuery(normal_dom)とすれば使えます.

// div_elmから全ての要素を削除したい
jQuery(div_elm).empty();

このjQuery()も便利ですがempty()が神すぎます.

ちなみにdiv_elmそのものを削除したい時にはremove()を使います.

// div_elmを削除
jQuery(div_elm).remove();

■便利!!要素にイベントを付加するbind()

前回のエントリでイベントリスナ関数について書いていましたが,これで十分でした.jQuery恐るべし.

// クリックイベントでクリックされた要素のvalueをアラート表示
$(#id_name).bind('click', function(){
    alert($(this).val());
});

はぁ・・・



■便利!!要素の生成

次は要素の生成です.これを使うととってもスマートにコードが書けて楽しくなります.
もう

var a_elm = document.createElement('a');
a_elm.setAttribute('href', './sample1.html');
a_elm.setAttribute('id', 'sample_link');
a_elm.setAttribute('target', '_blank');

などと書かなくていいと思うと胸が熱くなります.

上記のコードは

// $('<タグ名>')で生成
// attr({属性名:'値'})で属性を指定
var $a_elm = $('<a>').attr({
    href: './sample1.html',
    id: 'sample_link',
    target: '_blank'
});

と同じです.


注意すべきなのはマニュアルにあるのですが,input属性だけは生成と同時にtype属性を指定しなければならないそうです.

// input生成と同時にtype指定
// 指定する属性が一つであればattr('属性名', '値');でもおk
var $a_elm = $('<input>').attr('type', 'button');

また,css()でCSSプロパティを指定することもできます.

// div生成と同時にcss指定
var $div_elm = $('<div>').css({
    margin:'10px 5px',
    color: '#AAAAAA'
});

■注意!!css()のプロパティ名

上記のcss()はとても便利なのですが,たぶん多くの人が引っかかるであろう部分があります.
それは上記のようにプロパティを{}で指定した時です.
{}を使った場合のcssプロパティ名はハイフンを使ってはいけません!!!
例えば

// div生成と同時にcss指定
// これは動かない
var $div_elm = $('<div>').css({
    margin:'10px 5px',
    font-size: '16px'
});
// これは動く
var $div_elm = $('<div>').css({
    margin:'10px 5px',
    fontSize: '16px',
    fontColor: '#000000'
});
// ('プロパティ名','値')の場合は普通でおk
var $div_elm = $('<div>').css('font-size', '16px');

このようにプロパティ名をfontSizeやtextAlignという名前にしないと動きません.
これで何時間つぶれたかと思うと・・・.
本来はクラスをつくっておいてそれをaddClassするほうがいいと思いますけど.



■関係ないけど関数名を引数で渡して関数の中で関数を実行出来る

これはコードを参考に.

// 中で実行する関数を定義
function innerFunc()
{
    alert('関数内でこれを実行できた!');
}
// 外の関数定義
function outerFunc(fn)
{
    fn();
}

// こうすればアラートが実行出来ちゃう
outerFunc(innerFunc);

これは個人的に大ヒットでした.
自分はよく要素を生成してbind関数でイベントとコールバック関数を付加,という関数を書くのですが,その関数を使って同じような要素を生成する場合でも要素によって異なるコールバック関数を付加することがありました.そういう時にそのコールバック関数名を渡してあげれば同じ生成関数で出来ちゃうところがすごいと思いました.


とりあえずこんなところでしょうか.
javascriptjQueryは慣れるとホントに楽しいので,レガシーなjavascriptの書き方に飽きて,もういやだ!と感じた方は,ぜひjQueryとかjQueryUIとか使うのがお勧めです.

※いきなりjQuery使うとjavascriptってそういうもんだ,という先入観が出来てしまうので,まずは普通なDOM関数,レガシーな書き方を覚えたほうがいいかもしれません.

最近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最強伝説