Latest Publications

「DOMが取得できない」ときに読むといいかも。

細々と続けているこの開発者ブログも気がつけばJavaScriptの話題ばかりですね。

まぁ私しか更新しないので当然ですが(笑)あ、いや、私だけ暇だとかそういうんじゃないですから!

え~と、よくある、いいえ、よくありすぎる質問がいまだに頻繁に有りすぎるのでここらでまとめておこうかなと思います。

1.「getElementByIdで取得しているはずなのに取得できていません」とか、

2.「IEだと取得できるのに他のブラウザだと取得できません」とか、

3.「ここまでは取得できたんですが、ここから先が分かりません(?)」

上記のどれかに当てはまるならとりあえず以下の記事を参考にして欲しいですね。

1.「getElementByIdで取得しているはずなのに取得できていません」の場合

DOMツリーは構築されていますか?

DOM APIで操作する時、DOMツリーが構築される前に(要はページがロードされる前に)取得しようとするとnullが返ります。

nullに対してsetAttributeとかしてもそれは無駄なわけで、要素の取得にはDOMツリーの構築を待たなければいけないわけですよ。で、以下のコード。

window.onload = function()

{

var elm = document.getElementById(‘ID名’);

}

これで確実に取得できます。window.onloadでページのロードを待っているんですね。ページのロードが完了すれば、当然DOMツリーの構築も完了しているので、あとは取得し放題です(笑)これを逐一書くのが面倒な方は、ハンドラ関数を設定してそこから各処理を振り分けるのがよろしいかと。

あと、window.onloadだと重い画像の読み込みに時間がかかった場合、なかなか起動しないので、domReadyを使うのも一つの手ですね。Firefoxとかなら、

document.addEventListener(‘DOMContentLoaded’, listener, false);

みたいにハンドラ設定ができるので、より精密なタイミングで処理が出来ます。

IEとSafariは他のやり方が必要となります。詳細はググってみてください。

2.「IEだと取得できるのに他のブラウザだと取得できません」の場合

取得したID、実はname属性じゃないですか?

IE(7以前)では、getElementByIdではなんとname属性も取得できます。これはバグなんですがね。

というわけで、getElementByIdでnameから取得している場合、他のブラウザでは間違いなく動きません。

…まぁこの方法でやってる方は少ないでしょうが。

ちなみに、IE8ではこのバグは無くなり、ID属性しか取得できなくなっています。

3.「ここまでは取得できたんですが、ここから先が分かりません(?)」の場合

取得した要素から、さらに階層をたどっていくかもう一度APIで取得してみては?

例えば、var d = document.getElementById(‘hogehoge’)とかやって、id=”hogehoge”の要素を取得したとします。

そこから先、つまりその要素の中にある要素が取得できません、みたいな質問ですね。

この場合、取得した要素からたどっていく方法と、さらにAPIを使って取得する方法がありますね。

前者の場合、

d.firstChild  //要素の最初のノードを取得

d.childNodes //子要素の配列

とかで取得できます。但し、この場合は直接の子要素しか取得できません。孫要素を取得する場合は、

d.childNodes[1].childNodes[2]…….

と、何度も繰り返さないといけません。さらに注意。

子要素は、何もエレメントノードだけとは限りません。

d.firstChildで取得した場合、始めの子要素を取得するんですが、ソースを見やすくするために適宜改行を入れている場合は(実際はほとんどそうでしょうが)、かなりの確率で改行部分を空のテキストノードとして取得する場合が多いです。その場合、当然TypeErorとなりますね。よって、そのノードがエレメントノードかどうかを判定してから次のステップに進むほうがベターです。

例えば、

var e = document.getElementById(‘hogehoge’).firstChild; //要素の始めの子要素を取得

while ( e.nodeType !== -1) //ノードがエレメントノードでない限りループする

{

if (!e)break; //要素が存在しなければループ終了。

e = e.nextSibling; //要素の次の兄弟要素を取得

}

とかやれば、確実に最初のエレメントノードが取得できるんじゃないでしょうか。

続いて、後者(さらにAPIを使って取得する方法)の場合です。こっちの方が分かりやすいかも。

d.getElementsByTagName(‘タグ名’)とやって、さらにdエレメントの中から検索する方法です。通常、getElementByIdで基点ノードを取得して、そこからあれこれやるほうが効率がいいです。逐一documentから取得していると処理が重くなるし、何より余計なノードが混じりこむことがあるので面倒です。

例えば、

//id=”hogehoge”の要素の中のinput要素を取得する

var e  = document.getElementById(‘hogehoge’).getElementsByTagName(‘input’);

という感じで、メソッド連結で取得することが出来ます(この場合、返り値は配列っぽい要素リストです)。

このほうが要素だけを取得できるんでいいかもしれないですね。

但し一点注意が。

var e = document.getElementById(‘hogehoge’).getElementById(‘hogehoge2′);

これは動きません。

取得したエレメント要素にはgetElementByIdのAPIは用意されていませんのでご注意を。素直にgetElementByTagNameとかを使いましょう。

長々と書きましたが、参考にしていただければ幸いです。

(約)40行で出来るXML→JSONパース

ちょっとやってて感動したので書いてみる。

XHRで取ってきたレスポンスのXMLをパースしてJSONに変換するものです。

結構仰々しいのかなぁ・・・と思いつつ書いてたらあっさりできた。時間にして20分くらい?

折角なのでちょっコードをさらしてみます。

function XMLToJSON(ajax){

if (ajax.responseXML != null)var xmlDoc = ajax.responseXML;
else {
if (window.ActiveXObject) {
var xmlDoc = new ActiveXObject(‘Microsoft.XMLDOM’);
xmlDoc.async = false;
xmlDoc.loadXML(ajax.responseText);
}
else if (window.DOMParser) var xmlDoc =  new DOMParser().parseFromString(ajax.responseText, “application/xml”);
else return;
}
var xml = xmlDoc.documentElement;
var loopParse = function(obj)
{
var res = {}, cacheTag = {};
var ob = {}, att = obj.attributes;
if (att != null && att.length != 0) {
for (var a = 0, lenA = att.length; a < lenA; a++) {
ob[att[a].nodeName.toLowerCase()] = att[a].nodeValue;
}
res._attr = ob;
}

for (var i = 0, len = obj.childNodes.length; i < len; i++)
{
var ch = obj.childNodes[i];
if (ch.nodeType == 3)
{
if (ch.nodeValue.replace(/[\s|\t|\n]/g, ”) == ” || ch.nodeValue == null)continue;
else return ch.nodeValue;
}
else if (ch.nodeType == 1)
{
(ch.tagName in cacheTag) ? cacheTag[ch.tagName].push(arguments.callee(ch)) : cacheTag[ch.tagName] = [arguments.callee(ch)];
}
}
for (var p in cacheTag)(cacheTag[p].constructor == Array && cacheTag[p].length == 1) ? res[p] = cacheTag[p][0] : res[p] = cacheTag[p];
return res;
}
return loopParse(xmlDoc);

}

うーん、これだと本当に40行かどうか分からない・・・。

「マジかよ?本当に40行かよ?」という人はテキストエディタとかにコピーして適宜改行を消してみてください。多分大丈夫です。

でわ、何やってるかというと、まず引数にXHRオブジェクトを渡します。

で、最初にresponseXMLがあるかチェック、あればそれを使用。なければ、ActiveXObject(IE)、DOMParserを使ってresponseTextからXMLをパース。これならIEのローカル環境でも試せます。

で、肝なのがローカルで宣言しているloopParse関数。

Rootから子要素をリスト化し、ElementNodeかTextNodeかを判定、空でなければリストに追加、同じタグ名は配列にしまう、って感じです。属性もオブジェクトのしまってますね。あとはその子要素について同じ関数を再帰呼び出ししてパースしまくり、です。

おそらくひどいくらいにloopParse関数が呼ばれるので、出来るだけ軽くしてみました。単純なXMLファイルなら数~数十msでパースすると思います(Firefox3)。XMLベースならIEもそれなりの動作が期待できるので、処理を分ける必要性が少なくていいですね。

というわけで、XMLをJSONにするのに大きなライブラリは必要なかった、という結論に。XHRでとってこれればOK。

イベントの設定にもマナーを

某会社のCMみたいですが(笑)、人の作ったライブラリを見てると、時々イベントの設定をしっぱなしのものがあります。

PCでも電源を入れて、使い終えたら電源を切りますよね?それと同じで、イベントの設定をしたら、用を終えたらきちんとイベント解除しないといけないんじゃないかなぁ・・・なんて思います。

例えば、よくあるドラッグドロップ。

あんまり考えずに作ると、ドラッグするかどうかをフラグで管理する人がいますね(flag = trueならドラッグする、とか)。

これも一つの方法でしょうが、例えばmousedownでflag = true、mouseupでflag = falseにするとかします。

で、mousemoveではif (flag == false)return;とかやってるわけですが、これでも動くでしょう。

でも実は、マウスを動かす度にmousemoveのリスナは呼ばれてるんですよ!

ただ直ぐにreturnするだけですが、マウスを動かす度にリスナが呼ばれるとしたら、それはもう数百、数千回の単位で呼ばれ、悲しくも直ぐにreturn。

何も起こらないのでいいかもしれませんが、関数呼び出しには結構メモリを使います。mousemoveなんてかなりヤバイ。

なので、mousedownでmousemoveイベント設定、mouseupでリスナ解除、という風にちゃんとマナーを付けたいですね。

フラグ管理は万一の為のセーフガードとして私は使ってます。

attachEventとaddEventListenerの分岐も面倒くさがらずに・・・。

と書いておきながら、自分でも耳が痛いですね(笑)当たり前のようで、あまりやってない話でした。

第三回勉強会、無事終了しました

去る2009年5月23日(土)、無事勉強会を開催することが出来ました。

定員20名にも関わらず、多数のお申し込みを頂き、定員満了での開催となりました!

CSSは基礎の基礎、幾分かマニアックな内容も含めてセレクタからのスタート。本当にさわりくらいしか出来ませんでしたが、良い反響を頂けて感激しております。

PHPは制御構文(ループ)をやりました。内容的には「ああ、プログラミングなんだなぁ」という内容でした。

配列とかは挫折しそうな部分なだけに、しっかりとできたかちょっとだけ心配です・・・

ともあれ、またも時間が足りなくて出来なかったところ、途中で終わってしまった所を近いうちにリベンジしたいですね!

参加頂いた皆様、ありがとうございました!!

JavaScriptでMD5を作りたいけど・・・

PHPとかで結構使うMD5。暗号化した値の生成に用いるんですが、当然JavaScriptにはMD5関数なんてありませんよね。

となると自作しないといけないんですが、これがかなり手間がかかります。

私はアルゴリズムとか深い所まで理解していないダメな方なので^^;

といってもいずれは組み込まないといけないですね。参った。ビット演算とか微妙な理解度なんですよね~。

これだとただのつぶやきになってしまうので、とってもすごい方が作成したMD5化ライブラリを勝手に紹介しておきます。

http://www.webtoolkit.info/javascript-md5.html

とか、

http://www.kanasansoft.com/weblab/2006/12/javascriptmd5.html

とか、

http://rocketeer.dip.jp/sanaki/free/javascript/freejs17.htm

とかですかね。みなさん凄い。