Home > 技術

技術 Archive

Prototypejsのdom:loadedを検証(IEの場合)

いまさらながらPrototype.jsのdom:loadedがIEでDOMContentLoaded的な扱いになるか試してみた。
普通に使わせてもらっているから、ちゃんと動いているんだろうけど何せ擬似なDOMContentLoadedですからね。

Prototype.jsでは3960行目あたりからdom:loadedの部分になります。
んで、IE版のところは3988行目から。

こんな感じです。

scriptタグを生成して、ロードが完了したタイミングでセットされた関数をfireしています。
ここでscriptタグにはdefer属性が指定されているので、このscriptタグの中にはdocument.writeがないことを条件にしています。
つまり、この部分を遅延評価してね~という感じですね。

この遅延のタイミングがDomContentLoadedと同じ?とみなしているようです。

順番としては、

  1. ブラウザがHTMLを上から順番に評価し始める
  2. JavaScriptからscriptタグが追加されるが、defer属性が入っているのですぐに実行しない
  3. 画面全体のDomが構築される
  4. defer属性が付いていたscriptタグの中身が評価される
  5. よってこのタイミングがDom構築完了を意味する

ほんと?w
いやちゃんと動作するんだからそうなんでしょう。
defer属性をこうゆう風に使うなんて、なんかとっても面白いですね。

サンプルコード

少し重めの画像を適当に用意して、さきほどの擬似DomContentLoadedとwindow.onloadの中でalertを表示されています。
これでどっちが先に呼ばれるかが重要ですね。

結果はちゃんと「dom:loaded」→「onload」の順番でした。
ただし、上の画像がない場合など比較的HTML要素が少ない場合ではonloadのほうがdom:loadedより速かったです。
なので画像を使わないテキストサイトとかだとdom:loadedを待つよりonloadを使ったほうがよさそうですね。

ちなみにjQueryのIE版DomContentLoadedは

以下のように、document.documentElement.doScroll("left")が正常に動くまで実行しています。
これを考えた人は本当にすごいですね。
IEContentLoaded - An alternative for DOMContenloaded on Internet Explorer

なぜ、document.documentElement.doScroll("left")でExceptionが発生しないとDomContentLoadedだと思ったんでしょう。

補足

IEscript要素にdefer属性をつけるとinnerHTMLに代入したscriptが実行されるという仕様がある。


via: script要素のdefer属性の実装 - Thousand Years

なんてこともあるみたいです。

FirebugのmonitorEventsでイベント丸見え

いやはやこれは便利だ。
ここ最近全然使っていなかったが、あらためて使うと便利さが理解できる。

サンプル

で特定のエレメントのクリックイベントを監視。

で特定のエレメントのイベント全部を監視。

NodeList[0]とNodeList.item(0)では戻り値が違う

getElementsByTagNameとかで取得したNodeListから参照したいエレメントを削除して、それから中身を見ようとした場合elems.item(0)とelems[0]では戻り値が違う。
まぁ当たり前といっちゃ当たり前かもしれない。

item(0)は関数で指定したノードがない場合にnullを返す、[0]は直接NodeListを見に行ってそこに定義がないからundefinedかな。

とはいっても、

これはちゃんと通るから、大丈夫かな。

このほうが無難かも。

一応DOMの規定では配列のようにアクセスしても、item関数経由でアクセスしてもいいみたいです。

NodeListをforinするとわ~お

ちなみにNodeListをforinとかで回すとえらいことになる可能性があるので、注意が必要です。

これだと欲しいエレメントのpタグ以外に

  • length
  • item()
  • namedItem()

が取れちゃうから。
なので、普通にfor文で回したほうがよいですね。

高速化するなら

NodeListやHTMLCollectionに直接アクセスするより、一旦静的な配列にしたほうが速い - 素人がプログラミングを勉強するブログ

一旦配列に突っ込んだほうが速いようです。
意外や意外!

ブラウザごとのJavaScriptアニメーション比較

ここ最近JavaScriptでアニメーションする機会が結構増えてきたのですが、やっぱりブラウザによって速度というか動きがかなり違うので、その比較をメモメモ。

IE6.0

IE6.0は以外にもアニメーション処理は速いというか滑らかです。

前にもJavaScriptでアニメーション(animate)するときに気をつけたいことで紹介しましたが、中に画像がたくさんあるdivタグmargin-leftとかでアニメーションすると激重になる可能性があるので注意が必要です。

position:absoluteでのアニメーションはかなり調子良いです。

IE7.0

比較的問題がないブラウザです。

marginを使ったアニメーションにも強いですし、opacityを使って透過してフェードアウトとかにも強いです。
IE7.0は重い重いという話が良くありますが、アニメーションに限ってはなかなか出来るやつですw

Firefox2.0

これが一番曲者かと。

アニメーションする際に対象のdivタグがoverflow:autoになっていると、すごいチラ付きが発生しますし、アニメーション処理自体にもモッサリ感があります。

このoverflowの話は結構面倒で、今まではIEとIE以外のJSコードを分ける処理は結構書いていましたが、Firefoxの場合にもoverflowをhiddenにしたりautoにしたりと独自実装になる感じです。

Firefox3.0

Firefox2.0と比べるとまぁ良いのですが、タブをいっぱい開いていたりFirefoxのメモリ使用量が多いとアニメーション時にもたつく感じがあります。(これはFirefox2.0も同じです)

こちらもoverflowは対策が必要です。

Opera9

Operaのアニメーションはとてもきれいです。 margin、positionとも滑らかにアニメーションします。

ただLightBox風のJSを作る場合に、レイヤーを貼った上にボックスを表示してその表示方法がアニメーションのときにはちょっと注意が必要です。

input type="button"を押したらボックスを表示するとして、レイヤーが表示され始めているのにレイヤーの後ろにいるbuttonを押せてしまう場合があります。

つまりレイヤーが表示されたタイミングでbuttonのonclickをfunction(){}(つまり空)にしてやるとか、それ以外でフラグを使ってすでに押されていることを把握する必要があります。
(※これは画像ボタンにすることで解消されるかも?未検証)

Safari3.1

完璧なブラウザです。

特にアニメーションに限っては文句の付けようがない感じです。
Safariの欠点は、Safariでは画像のloadが終わっていないとwidth、heightがうまく取得できないでも紹介しましたが、jQueryのreadyではDOM構築が終わっていない場合があるので、アニメーションさせたい対象のエレメントの幅を取得したい場合はonloadを待つほうが無難かもしれません。

Google Chrome

Safariと同じWebkitベースのブラウザなので、同じくキレイです。

ただ、ちょっと未確認なんですがJSファイルをBOMありのUTF-8で保存した場合に、うまくJSファイルを読み込んでくれなかったです。

他のブラウザでは問題なかったのですが、Google Chromeだけで発生しました。
対応としては、BOMなしのUTF-8Nとかで保存しました。
う~ん。

Netscape7

アニメーションはFirefox2.0よりは滑らかに感じますが、やはりちょっと微妙です。

各ライブラリがNetspaceに対応していないのもあり、opacityの透過などは自前で容易する必要もあったりします。
一応Netscapeではfloatしているブロックにrelativeをかけても効かないでちょっとした欠点も載せているので参考にしてください。

これ以外にアニメーションと関係ないですが、CSSで
background: url(/hogehoge.gif) no-repeat left 7px;
みたいにピクセル単位で座標を指定する場合がありますが、これにはNetscapeは対応していません。
いろいろ試してはみたのですが、ダメでした。
なので、
background: url(/hogehoge.gif) no-repeat left center;
などで対応。

まとめ

全ブラウザで同じように見せるスタイルシートも大変な作業ですが、JavaScriptを使ったアニメーションもブラウザの個性に対応しないといけないので、まだまだクロスブラウザ対応は必要そうですね。

Ajaxのオブジェクト(XMLHttpRequest or Msxml2.XMLHTTP)や基本的なDOM操作はライブラリが吸収してくれますが、アニメーションに限ってはどのスタイルを使ってアニメーションをするかは自分で決めなきゃいけないので、これからいろいろ調べていきたいと思います。

とはいってもjQueryのanimateメソッドにはすっごく助けられていますがw

■参考記事
jQueryのanimateメソッドの使い方
JavaScriptでアニメーション(animate)するときに気をつけたいこと

Netscapeではfloatしているブロックにrelativeをかけても効かない

ネスケのクロスブラウザ対応なので、あんまり必要はないかもしれないですが、
一応こんな現象を発見したのでメモメモ。

以下のように外枠のブロックに「float: right;」と「position: relative;」が掛かっている状態で、
中にいるブロックに「position: absolute;」で絶対配置にしています。

こうゆう例はちょっと珍しいですが、中にいるブロックが複数あってz-indexで切り替えるときとか
こんな感じかと思います。


んで、上の例だとネスケではrelativeが効かず、画面の左端に中のブロックが移動してしまいます。

これを解消するには「float」と「position」を別々のブロックにしてあげるとうまくいきます。

解決策

何かと何かを一緒のブロックに指定するとうまくいかないケースはIEだけかと思っていましたが、
以外にもネスケでこうゆうパターンがありました。

参考までに!

onloadを待たずに特定のエレメントに処理を実行する方法

ちょっとonloadを待たずに処理するのはどうやろう・・・みたいな疑問が沸いたのでメモメモ。

onloadイベントはwindowオブジェクトやimgタグ、scriptタグなど特定のものにしか存在しないので、擬似的に対象エレメントのDOM構築が終わったかどうかの判定ができません。
onload

なのでタイマーを使ってエレメントが取得できるまで繰り返し、その後に処理を実行するという方法ならDOM構築完了時に処理が実行できそうです。

たとえば上記のようなエレメントがあって、ここからid="hoge"のinnerHTMLをonloadイベントを待たずに取得したい場合は
以下のように書く感じです。

特定エレメントのidプロパティが取得できたらDOM構築が完了と考えていますが、もしかしたらこれだけだと判定としては弱いかもしれません。
ここは要調査!

画面の高さが結構ある画面なんかで画面初期でselectboxをdisabledにしたい~というときにonloadを待っているとかなり時間が経った後にdisabledになるので、こうゆう方法もありかもしれません。
DomContentLoadedでもそこそこ時間がかかるはずっ。

※divタグ自身にonloadがあったらな~。

jQuery Pluginの書き方

※10/14 もう少し詳しく書いてみる!

jQueryのプラグインなんかを書き始めると、どうゆう風に記述するかいろいろ人によって違うのでちょいとまとめてみました。
おそらくこれ以外にもありそうですが、比較的メジャーな感じで並べています。

jQueryには
  1. ①$("#hoge")で取得するオブジェクト
  2. ②jQueryオブジェクトそのもの

と2つのオブジェクトが存在します。
違いは①のほうは「$("#hoge").hogehoge();」という感じで書くことができ、②のほうは「jQuery.hogehoge();」とちょっとしたユーティリティのように書けます。
メソッドが実行されるタイミングで対象のエレメントが特定されてて欲しいなら①、そうでないなら②みたいにボクは使っています。

これを踏まえて以下のコードはjQueryオブジェクトにそのまま追加するパターンとして紹介しています。
もし①で使えるように追加するなら「$.fn.hoge」というようにfnを途中に追加してください。

一般的な書き方

よく見るコードはこの書き方かと思います。
関数の中では$で記述できるので比較的楽です。

ちょっと普通な書き方

これは1番目とあんまり大差はないですが、これも$で書けるのでまぁ楽チン!

jQueryオブジェクトをそのまま使う書き方

これは毎回jQueryドットと書かないといけないので、ちょっと面倒。
やっぱり1番目のほうがスマートですかね。

関数スコープに閉じ込めない書き方(これは良くない)

う~ん、これはjQueryっぽさがなくなっちゃうし、関数の外で書いた変数はスコープグローバルになっちゃうし・・・
バッドノウハウですねw


あと肝心なもう一個を記述し忘れてましたw

extendを使う書き方


以下jQueryの解説スライド。
こうゆうのをちょっと覗いてみるのも面白いですよ!

UTF-7でスクリプトを実行させるXSSについて

IEでUTF-7なスクリプトをiframeの中に表示するとXSSが発生するメモです。
あんまりこの話には詳しくないので、今後の調査材料として残しときます。
(※一応悪用厳禁であります!)

iframe内のエンコードが正しく指定されていない場合は、そのiframeの親にあたる部分のエンコードが自動的に適用されるというIEのバグがあるみたい。
今回はそれのちょこっとした検証!

1番初めに呼ばれるhtml(①.html)

iframe内で呼ばれるGetメソッド

結果

doGetメソッド内でエンコードを正しく設定せずにレスポンスしているため、IEでalertが表示される。

その他の実験まとめ

  1. 「①.html」ではなくiframeのパスを直接呼んだ場合はscriptは実行されない
  2. 「①.html」にmetaタグを正しく指定すればscriptは実行されない
    <meta http-equiv='content-type' content='text/html;charset=utf-8'>
    ただしutf-7を指定した場合はscriptは実行される(まぁそうだろうな)
    <meta http-equiv='content-type' content='text/html;charset=utf-7'>
  3. サーバー側でエンコード指定してもIEが解釈できないエンコードの場合はscript発生
    arg1.setContentType("text/html");
    arg1.setCharacterEncoding("utf8");	//正しくはutf-8
    
  4. 当たり前だがiframe内のhtmlに正しくmetaタグを指定すればscriptは実行されない

このお話はなかなか奥が深くてむずかしいですね。
とにかくIEに依存した Webアプリケーション セキュリティのプレゼン資料を見て勉強するしかなかそうです!


■参考にさせていた資料
IEに依存した Webアプリケーション セキュリティ
そろそろ UTF-7 について一言いっとくか - 葉っぱ日記
まだまだあるクロスサイト・スクリプティング攻撃法:ITpro
36. UTF-7とクロスサイト・スクリプティング:ITpro

(function(){})()の代わりにnew function(){}って方法もあるよ

だいたいは①の方法で即時実行な無名関数を作ると思うんですが、②みたいに書くこともできる。
でもボクはやっぱり①の書き方のほうが好きだな〜。

ただし、②の使い方にはちょっと気をつけないといけないです。
というのも②はfunctionをnewしちゃっているので、この関数はコンストラクタになってしまうこと。
なので以下のようにその関数が属するオブジェクトが変化してしまします。

もうちょい分かりやすく書くと以下の感じ。

func2のほうはnewした段階でコンストラクタが呼び出されるので、thisが代入先のobjに切り替わる。

ということでやはりnew function(){}よりも(function(){})()のほうが無難そうですねw
jQueryなんかもコード全体を(function(){})()で囲って、スコープを関数内に閉じ込めたりしてるし。(理由になってない!)

canvasを使って波紋が広がるripple.js作りました

ripple

canvas使って何かしたいな〜と思っていたら、波紋を描いたらちょっと面白いかもと思ってこんなJavaScript作ってみました。
あんまり深いこと考えてないですw

マウスの移動に波紋が付いてきます。
またクリックすると少し大きい波紋が広がります。

一応コードは以下の感じ。
canvasを使って丸を描いてます。(すごいシンプルっ!w)

またイベントのセットは以下のようにmousemoveclickに割り当ててます。
すいません、今手元にIEがないのでIEのチェックだけ出来ていないです。多分以下のコードで動くような〜。

Eventが発生するたびにマウスの座標をRippleクラスに渡しています。その後drawメソッドで描画しています。

とりあえず触ってみてください。
ripple.jsを触ってみる

ちょっと気に入っているのはこういったアニメーションするときに少し描画を遅らせるとそれっぽくなる気がします。
なので、マウスを移動したときに少し遅れて波紋が付いてくると思います。
こうゆうの結構好きです。

ソースは以下からダウンロードできます。
ripple.js


他にもこんなん作ってます。どうぞよろピク。
キングボンビーがサイトを侵略する?jQuery Plugin - kingbonbi.js作りました
なんかものが落ちるjQuery Plugin - JDropper作りました

Chromeのユーザーエージェント

一応メモ。
Webkitやね〜。

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.X.Y.Z Safari/525.13

[via]
CyberAgent SEO Information

WebkitのDOMContentLoadedはDOM構築が完了していないかもしれない

Safari3.1でDOM構築が終わったタイミングでイベントを登録していたのですが、うまくいかなかったときのお話です。
jQueryを使っていたので自動的にDOMContentLoadedイベントに登録されると思っているのですが、これがどうもDOM構築が終わっていないんじゃないかと。

それが判明したのが、画像をスライドするパーツを製作していたときにスライド対象のdivタグのwidthを取得していて、このwidthがなぜかSafari3.1だと小さく取得されました。

以下にその例を載せて起きます。

SafariのDOMContentLoadedで失敗したパターン

画像がたくさん並んだボックスを左や右へスライドさせるときに、何枚あるか分からなかったので画像が入っているボックスのwidthを取得して計算をしていました。
以下にサンプルのHTMLを載せています。

このページの全体の高さは2500pxほどあります。ちょっと縦長なページ。
わけあってdivが2階層いますが、スライドさせる対象のエレメントはulタグになります。
このulのmargin-leftやmargin-rightを変更することによって画像がスライドしているように見せる感じで作っていました。

んで画面のDOM構築が終わった段階で、ulタグの幅を取得しようとしたのですが、なぜかSafari3.1でだけwidthが少なかったのです。
たとえばFirefox3では1000pxあったのに、Safari3.1では650pxとか。
かなり中途半端なwidthが取れていたので、これはもしやDOM構築終わってないんじゃ?
と思ってwindow.onloadにfunctionをセットしてみたところ・・・


「Safari3.1でもwidthが1000pxで取得できました。が~~~ん」

jQueryのreadyの中身について

久々にjQueryのreadyの部分を追ってみました。
bindReadyというメソッドが2329行目あたりにあります。
その中に以下の記述があるのですが、これは「webkit nightlies currently support this event」とコメントされているとおりSafari3.1ではDOMContentLoadedがあるのでif文に入ります。
参考リンク:Changeset 26101 -- WebKit -- Trac

そしてこのDOMContentLoadedを登録しつつ、その先に進みます。
2366行目あたり。

ここでSafariの場合に2つの方法を使ってDOMContentがLoadedになったかを確認しています

まずdocument.redayStateがloadedかcompleteになるまで自分自身の関数をループします。
ここのチェックが終わったらstyleタグとlinkタグの合計数とdocument.stylesheets.lengthが一致するまで繰り返します。

これら2つの条件をクリアしたらやっとDOMContentがLoadedになった状態とみなすようです。
なかなか複雑ですねw

ここでちょっと気になるのですが、Safari3.1では「DOMContentLoaded」がありますが、その後の「if ( jQuery.browser.safari ) {」にも入ってしまうのです。
なので2重のほうほうでDOM構築を待つ感じになっています。どっちのほうが速いの?w

ちょろっと簡単にテストしてみました

上記のコードを実行した場合の挙動が少しへんでした。
まずDOMContentLoadedでjQuery.readyが登録され、その後のSafariの分岐でdocument.readyStateの状態を監視します。
するとdocument.readyStateのif文には約1回くらいしか入らず、その下にあるif文には一切入りませんでした。

順番にしてみました。

  1. DOMContentLoadedにjQuery.readyを登録
  2. Safariの場合にdocument.readyStateを監視
  3. DOMContentLoadedイベント発生でjQuery.isReadyがtrueになる
  4. document.readyStateを監視しているがjQuery.isReadyがtrueなのでループを抜ける
  5. だからdocument.stylesheetsのlengthチェックが走らない

う~~~ん・・・
だとするとSafariのDOMContentLoadedのほうがjQueryのDOM構築待ちより速いということでしょうか。
速い故にまだ構築し終わってない?w
まさかね!

まとめ

かなりイレギュラーなパターンだと思いますが、Safariでこういう現象があったのは確かです。
なのでwindow.onloadを待つか、documentのDOMContentLoadedを待つかはテストしながら判断したほうがよいかもしれません。

Firefox3.0やIE7、IE6では特に問題がなかったです。
SafariのDOM構築って少しくせがあるので、そこが悩みどころですね。
レンダリング超速いですが・・・

JavaScriptで単体テストをするならQUnitはいかが?

「JavaScriptのテストってわざわざXUnit系を使ってまでする必要はない!」
みたいな意見は多々あるのですが、ライブラリなどを作っているときには非常に便利だったりします。
ちょこっとした修正がどの程度影響があるか分からないときに、テスト用のコードをしっかり書いてあればそれを実行するだけですんでしまいますからね。

そんで最近になって
JavaScript--単体テスト環境の選択肢 - builder by ZDNet Japan
という記事を見てて、「あれ?Qunitがないぞ!」と思ったので簡単に使い方を書いてみたいと思います。

上記リンクの記事ではJsUnitRhinoUnitのことが書かれていますが、これらはJavaありきというかANTありきなので、ちょろっとテストコードを書くというスタンスにはなかなか難しいと感じています。
ただJsUnitは全ブラウザを登録しておけば、いっきにクロスブラウザのテストを実行してくれるので便利といえば便利なんですけどね。

その点QUnitはjQuery用のテスティングフレームワークなので、コード量も少なく簡単にテストコードを書いていけます。
(少しだけjQueryに関する知識が必要になってきちゃうのはご愛嬌)

jQueryを使ったテスティングフレームワークですが、別に自作のライブラリのテストももちろんできますので、開発で使っているライブラリをテストすることができます。

使えるメソッドいろいろ

QUnit - jQuery JavaScript LibraryのAPI documentationのところに使い方が書かれていますので、ここではよく使いそうなメソッドだけを説明したいと思います。

test( name, test )

nameにはテスト名、testには実行したいテストコードを匿名関数で記述します。

module( name )

nameにはテストしたい単位を渡します。これはテストをしているときの目印になります。
たとえばレイヤーを操作するテストをしたい場合に

としておくと、テスト結果画面で
「レイヤー表示テスト module: レイヤーの枠を表示する (0, 1, 1)」

みたいにmoduleの手前にプレフィックスが表示されます。

ok( state, message )

stateにはfalseとtrueを指定できます。
falseの場合にはNG、trueの場合にはOKとなります。
これは帰ってきた値がtrueかfalseか、または通過したかしないかなどの判定に使えそうです。

messageは表示したい文字列を渡します。

equals( actual, expected, message )

これはよく使うやつですね。
actualとexpectedの値が同じ場合にOKとなります。

messageは表示したい文字列を渡します。


すぐに使いそうなのをご紹介しましたが、もっと詳しく知りたい場合にはjQueryのテスティングフレームワークQUnit (でぃべろっぱーず・さいど)をごらんください。
「提供されているメソッド」に詳しく書かれています。

QUnitのダウンロード

QUnit - jQuery JavaScript LibraryのUsing QUnitのところにjsとcssのリンクがあるのでここからダウンロードできます。

ダウンロードするのが面倒というかたは次の「QUnitを使ってみる」に進んでください。

QUnitを使ってみる

以下がQUnit用のHTMLになります。
ここにテスト結果がどんどん表示される感じです。

もしQUnitをダウンロードされた方はtestsuite.cssとtestrunner.jsのパスを変更してください。

今回はテスト用のコードを「qunit.js」に、テスト対象のコードは「hogehoge.js」として話を進めていきます。

テストしたいコードは以下になります。

文字列を返してきているので、これはequalsでチェックできそうです。
なので、qunit.jsには

と記述してテストを実行してみました。
実行結果は以下の画面になります。

failedが0になっているので、バグはなさそうですね。
おそらく大体がこういったテストで十分だと思うのですが、もしHTMLエレメントを使ったテストの場合には少し工夫が必要です。
どっかのdivタグのinnerHTMLを書き換えたテスト、id指定で取得したエレメントのwidthのテストなどはどうしてもHTMLを必要とします。
次はHTMLエレメントに対してテストをする場合を解説してみたいと思います。

HTMLエレメントに対するQUnitテストコードを書いてみる

今回はHTMLエレメントを透過させるメソッドをテストコードを書いてみました。
初めに必要になるファイルを解説します。

  1. qunit.js - テストコードが記載されているファイル
  2. hogehoge.js - テスト対象のコードが記載されているファイル
  3. opacity.html - 透過のテストをするときに使うHTML

透過に関するテストって自動でできるの?と考える方がいらっしゃるかもしれませんが、これが以外とできるんですw

先ほど使ったQUnitテスト結果HTMLは、あくまでもテスト結果を表示するものなのでこれとは別にHTML(opacity.html)を用意します。

まずは透過させるコードです。

これのテストコードは以下になります。

ここからがちょっと新しいテストコードになります。
テストコードの中でtestwinというメソッドを呼んでいます。これは新しくウィンドウをPopupさせ、そのHTMLの描画が完成した瞬間にテストコードを実行してくれるすぐれものです。
つまりopacity.htmlを新しいウィンドウで開いてDOMの構築が完了したら透過のテストコードを実行してくれる感じです。

これはjQueryのoffset.jsの中を参考にしています。

続いてopacity.htmlの中身。

ここではjQueryのファイルとテスト対象のhogehoge.jsを読み込んでいます。
そしてscriptタグで「$(function(){});」を実行しています。
先ほどのテストコードでPopupした画面のDOM構築を待つと解説しましたが、これはつまりjQueryのjQuery.isReadyがtrueになるのを監視しているのです。
jQuery.isReadyはreadyというメソッドが呼ばれて初めて監視がスタートされるので、ここで$(function(){});を実行しているのです。
ちょいややこしいですねw

これでテストするためのコードがそろいましたので、実行してみたいと思います。

おお~Opacityのテストが実行されました。
$w("#opacity").setOpacity(0.2).getOpacity()で返ってきた結果が0.2なのでちゃんと透過機能がはたらいたようです。

まとめ

HTMLエレメントのテストで別のHTMLファイルを作らないといけないのはちょっと不便かもしれませんが、1回作ってしまえば後は何度でもテストが可能ですので、いつかきっとプログラマーを助けてくれると思います。

QUnitはJavaやANTなどが不要なため、完全に独立したテスティングフレームワークとして使えるので
「JSでテストコード書きたいけど敷居が高いな~」
という人にはもってこいなのではないでしょうか?

良く分からないこと

IE7.0でウィンドウを開いてテストするときに、まれにうまくいかないことがあります。
F5とかで再度テストをすれば大丈夫ですが、そうゆうときもあるようです。
Safari3.1やFirefox3では特に問題なかったです。
でもなぜIEだけ・・・

■関連リンク
Getting Started With jQuery QUnit for Client-Side Javascript Testing - Chad Myers' Blog
jQueryのテスティングフレームワークQUnit (でぃべろっぱーず・さいど)

onunloadイベントが拾えないパターン

onunload時に何か処理を実行して、その処理がとっても大切な場合は以下のパターンのときに
注意する必要があるかもしれないというメモ。

リンクを押して他の画面に遷移した場合

Firefox3.0
OK
IE6.0
OK
IE7.0
OK
Safari3.1
OK
Opera9.5
OK


F5を押した場合

Firefox3.0
OK
IE6.0
OK
IE7.0
OK
Safari3.1
OK
Opera9.5
NG


ブラウザを閉じた場合

Firefox3.0
OK
IE6.0
OK
IE7.0
OK
Safari3.1
NG
Opera9.5
NG

JavaScriptからOpacity(透過)を設定する方法

JavaScriptでアニメーションさせようとしたとき、意外と使うのがこの透過。
じょじょに透過させていくとか、見栄えもかっこよくボクは良く使います。

でも既存ライブラリを使った場合、Netscapeに対応していない場合が多いです。
jQueryPrototype.jsはそもそも対応ブラウザにNetscapeが含まれていないので当然といえば当然ですね。
それでもNetscape対応させたい場合のメソッドを作ってみました。
すんごい短いコードですw

一応ライブラリごとのOpcityの設定について書いておきます。

jQueryからOpacity

PrototypeからOpacity

自作メソッドからOpacity

3パターンのOpacity設定を使って、どのブラウザでも透過ができるようになっています。

JavaScriptやCSS、Railsなど使えそうなチートシートいろいろ

Cheat Sheets - Added Bytesで開発に役立ちそうなチートシートが公開されています。

PDFで落とすことができますが、PNGもあるのがありがたい。

チートシートもみやすく前にPrototype.jsのチートシート(ver1.5、1.6)で紹介したものを合わせてどこかに貼っておけばきっと開発を助けてくれるはず。


JavaScript Cheat Sheet


JavaScript Cheat Sheet - Cheat Sheets - Added Bytes

CSS Cheat Sheet


CSS Cheat Sheet (V2) - Cheat Sheets - Added Bytes

JavaScriptライブラリ毎のSelectorAPIの速度一覧

普段はPrototype.jsjQueryを使ってJavaScriptを書いているのですが、そこまで複雑なCSS SelectorAPIは使ったことがないのでこうゆう一覧、というか実際に実行して確認できるのはありがたい。

SlickSpeed Selectors Test

SlickSpeed Selectors Test

検証してくれるJavaScriptライブラリは以下のとおり。

  • DOMAssistant2.7.2
  • jQuery1.2.6
  • Prototype1.6.0.2
  • Mootools1.2
  • ExtJS Core2.2
  • Dojo1.1.1
  • YUI2.5.2

MacのSafari3.1で試してみたのですが、DOMAssistantというライブラリがおそるべき速さで実行できています。
もっとも遅かったのはYUIですが、Prototypeもそれに負けずに遅い感じでした。

jQueryはさすがですが、Extに負けているのがビックリ。

いろんなブラウザで試せばこれで一通りのベンチマークになりますね。
とはいっても複雑なCSS SelectorAPI書くより、idふるなり適当なclassを割り当てたほうが良さそうですねw

addEventListenerとattachEventでは実行される順番が違う

たとえばPrototype.jsを使っている場合、

こんな感じでイベントを登録することができる。
また同じオブジェクト(window)に対して同じイベント(load)を指定した場合は、追加として登録される。
なので上書きはされない。

こうゆう場合にIEとIE以外では挙動が違うので一応メモを残しておきます。

IEのattachEventの実行順序

一番最後に追加されたイベントから一番最初に追加されたイベントにさかのぼるように実行される。

なので上記のコードでは2→1

IE以外ののaddEventListenerの実行順序

一番最初に追加したイベントから一番最後に追加されたイベントを実行される。

なので上記のコードでは1→2

まとめ

初めPrototype.jsが何かしているのかと思ったのですが、とくにそれっぽい箇所がなく以下のように自作した関数でも再現したため、こうゆう挙動は一般的なんでしょうね。

一番最初に登録したイベントから実行されるのを望む場合はIEとIE以外で挙動が違うのでハマる危険性がありますね。

追記:
IEのイベント実行順序は不定? - inamenaiの日記でさらに実験をしていただきました。
attachEventは単純に逆順になるわけではなくランダムだそうですw
でも何度か実行しても同じ順序になるようなので、何か法則でもあるんでしょうか・・・

CSSファイル内に他のCSSファイルをインクルードする方法

ひとつのCSSファイルを共通部分をあの画面とこの画面とかで共通で仕様したいがlinkタグを追加したくないとかに便利。

読み込むCSSは1つのファイルなのに、CSSファイルの中から別のCSSファイルをインクルードしてくれるので、2個のCSSファイルを読み込んでくれる。

index.html style.css include.css


あんまり使う機会はなさそうですねw

DOM Events - DOMAttrModifiedを使ってみる

イベントを登録されたオブジェクトの属性が変更された場合にfireされるイベント。
Document Object Model Events

あるHTMLオブジェクトの属性を監視するときに便利かもしれませんね。

setIntervalで監視しているのをこういったイベントに置き換えることによって、簡略化もできるし。
でもFirefox限定・・・

DOM Events - MDC

DOMとJavaScriptについて

DOM(Document Object Model)とは、HTML文書およびXML文書のためのAPIである。」
というのがDOMの説明に多いのだが、一体何がDOMで何がDOMではないのかを判断するのがちょっぴり難しい。

JavaScriptをコーディングしていて、「あ~この部分はDOMだな」とか「う~ん、ここはDOMではなくJavaScriptだな」とかを明確に判断する必要はないんだけれど、分かればもっと楽しくなるんじゃないかな。

なのでDOMで遊ぶ際に必要な知識なんかを以下にまとめてみました。
既知の情報も多々ありますが、個人的なメモなのでご了承ください。

DOMとは?

HTML(XML)のようなタグ構造を持つ文書に対して、あたかもオブジェクトのようにアクセスするためのAPIです。 HTMLはタグという文書構造になっているので、例えば「
」のようにidにhogeという文字を持つdivタグにアクセスしたいとしたとき、その方法を提供してくれるのがDOMの機能です。 document.getElementById - MDC

文書の構造なのにJavaScriptからオブジェクトのようにアクセスできるのはとっても大切で、表示している文字列を書き換えたり背景色を変更したり、アニメーションさせたりといろんなことができてとっても楽しいのです。

そういった便利な関数やプロパティはオブジェクトとして提供されDOMインターフェースというものを通じてボクらはアクセスすることができます。

たとえば以下のようにtableタグを作るためにdocumentオブジェクトが持つcreateElementメソッドを使ったとしましょう。
この場合var宣言で定義されたobjには何が入っているのでしょうか?

JavaScriptでいうところのオブジェクトなのには変わりないのですが、new Object()とした場合と今回のようにcreateElementした場合ではオブジェクトの中身が全然違います。
「HTMLTableElementというDOMインターフェースを実装したtableオブジェクト」というのがobjに入っているオブジェクトの正体ですね。
(なんだか長ったらしいですが・・・w)

createElementでtableオブジェクトを作った場合にはinsertRowというメソッドを持ちますが、new Object()ではそのようなメソッドは持っていません。
これはHTMLTableElementインターフェースを実装しているかそうでないかの違いです。
HTMLTableElement (Common DOM API)

DOMって他の言語でも使えるの?

DOMの機能はPerl・Java・ActiveX・Pythonなどで提供されているようです。
確かにXMLを読み込むような処理の場合にDOMが使えれば簡単にアクセスできますもんね。

PythonのDOMを使った例

# Python DOM example
import xml.dom.minidom as m
doc = m.parse("C:\\Projects\\Py\\chap1.xml");
doc.nodeName # DOM property of document object;
p_list = doc.getElementsByTagName("para");


via: 導入編 - MDC

JavaのDOMを使った例

初めにXMLの読み込み処理がありますが、それ以降はほとんどJavaScriptで使ったものと同じですね。

このようにDOMは他の言語でも提供されているありがた~いオブジェクトなわけです。
これもDOMというものを中立に考えて機能を実装してくれているからなのでしょう。

何がDOMで何がJavaScript?

ここが本題といったところでしょうか。
どこがDOM?みたいに疑問を持つことは普段のコーディングにあんまり影響はしませんが、たとえばwindow.alert()ってDOMなの?そうじゃないの?みたいに少し突っ込んでみると興味が沸いてくると思います。

alert() は引数の文字列が入ったダイアログをポップアップする DOM のメソッドです。


via: The DOM and JavaScript - MDC

とmozillaで解説されているので、これは間違いなくDOMの機能でしょう!

では以下でDOMの場所を特定して解説してみたいと思います。
なおThe DOM and JavaScript - MDCのDOM と JavaScript - 何が何をしているのか?を参考にしています。というかほぼ一緒!


サンプルコードたったこれだけです!w
これだけあれば十分です。

var obj =

ここはobjというJavaScriptの変数を作成しています。

document.getElementsByTagName("div");

Documentインターフェースを実装しているdocumentオブジェクトのgetElementsByTagNameメソッドを使ってHTMLページの順番でdivタグを取得しています。
ここで取得されるオブジェクトはNodeListインターフェースを実装している配列になります。
なのでlengthというプロパティを持ってはいますが、これはJavaScriptの配列のlengthとは違います。
NodeListインターフェースが提供しているlengthとJavaScriptが提供している配列という違いですね。

alert(

DOMの機能を使ってダイアログを表示します。
(今までにこのメソッドを何度使ったことか・・・最近はconsole.logになりつつありますが)

obj.item(0).id);

ここはちょっぴり複雑ですね。
まずobjにはNodeListインターフェースを実装した配列が入っています、そしてその一つ一つにはHTMLDivElementインターフェースを実装したオブジェクトが入っています。
NodeListインターフェースにはitemというメソッドがあり、引数に数値を指定することでdivオブジェクトを取得することができます。

そして取得したdivオブジェクトが持つidというプロパティにアクセスしていますね。
idというプロパティはElementインターフェースが提供しているプロパティです。
ということはHTMLDivElementインターフェースとElementインターフェースを実装しているということになります。
(2個も実装しているなんてなんて贅沢なっ!)

基本的なclassName、id、innerHTML、styleなどは確かにどのHTMLオブジェクトでもアクセスできるので一元的にElementインターフェースで実装しているんですね。
ほほぅ。
element - MDC

たった2行のJavaScriptを見てみましたが、こんなにもいろんな実装がされているなんて!といった感じでしょうか。
先のコードでDOMじゃない部分といえば、変数宣言のvar obj部分と;セミコロンくらいです。
それ以外が全部DOMで提供された機能だったというわけです。
DOM様様ですねw

まとめ

なんとなくDOMについてまとめておきたかったので今回の記事を書きましたが、結構ボリューム感がありました。
とはいってもmozillaのサイトに行けば、JavaScriptの仕様はたいてい記載されていますし、今は詳しく書かれた本もたくさん発売されています。
勉強するにはもってこいな時代ですね。
6年くらい前はまだAjaxもそんなに普及されていなくて、そもそもAjaxという言葉はなかったからずうっと「非同期」なんて呼んでいたりしました。
その当時はwindow.alertはDOMの機能だってことは、どこにも書いてなくて先輩とかに聞いて回ったのを覚えています。

なんかものが落ちるjQuery Plugin - JDropper作りましたで作ったオモロ~なプラグインもDOMの機能なしには語れないのです。

DOMを学びやすいJavaScript解説本

JavaScript 第5版
JavaScript 第5版
posted with amazlet at 08.09.02
David Flanagan
オライリー・ジャパン
売り上げランキング: 7060

やっぱりサイ本はかかせないです。
この本は重要なところを太字とかにあんまりしてくれていないので文章を読む感じで学習するのが得意な人に向いているんじゃないでしょうか。

Head First JavaScript 頭とからだで覚えるJavaScriptの基本
Michael Morrison
オライリージャパン
売り上げランキング: 4564

この本最近買ったのですが、アツいですw
イラストばんばん使いまくりで、クロスワードとかも途中にあって、学習というより遊びながらな印象があります。

まるごとJavaScript & Ajax ! Vol.1
天野 仁史 舘野 祐一 川崎 有亮 arton 田中 孝太郎 国分 裕 山本 有悟 海野 裕也 nanto_vi
インプレスジャパン
売り上げランキング: 125084

かゆいところまで突っ込んで解説されているので、かなりな良本です。
雑誌感覚なので、気軽に読めるのもいいですね。


■関連リンク
W3C Document Object Model
Gecko DOM Reference - MDC
org.w3c.dom (Java 2 Platform SE 5.0)

Arrayをforinするより普通にfor文で回すほうが速い

Arrayのlengthはいったん変数に格納してからループしたほうが速いに引き続きベンチマーク。

そういえば配列をforinで回したことがあんまりなかったので、普通のfor文とどっちが速いのかな~という疑問が沸いたので試してみた。

配列をforinで実行した場合

Firefox3.0
481ms
IE6.0
3234ms
IE7.0
219ms
Safari3.1
219ms
Opera9.5
234ms

配列を普通のfor文「for (var i = 0; i < a.length; i++)」で実行した場合

Firefox3.0
7ms
IE6.0
32ms
IE7.0
16ms
Safari3.1
16ms
Opera9.5
31ms


この結果でびっくりしたのが、forinで回した場合のIE6.0の遅さ
じゃっかんブラウザも固まりかけていました。

配列に対して以下のように3つしか値が入っていないのに、lengthが1001になる場合にはforinのほうが有効かもしれませんが、そうゆう使い方はあんまり配列ではしないのでやっぱり普通にfor文で回したほうがよさそうですね。

さらにlen=arr.lengthのように一旦lengthを退避すればなお良いということかな。

■関連リンク
JavaScriptの配列をも~っと深く理解する:lengthの不思議な動作 - page2 - builder by ZDNet Japan

Arrayのlengthはいったん変数に格納してからループしたほうが速い

既出な内容ではあるが、一応メモとして残しとく。

配列を走査するときに、配列のlengthを一旦変数に格納したほうが速いんじゃないかという話。
実際試してみた。

コードは以下の感じでどのブラウザでも実行できるようにしました。

配列のlengthを退避せずに実行した場合

Firefox3.0
12ms
IE6.0
63ms
IE7.0
63ms
Safari3.1
31ms
Opera9.5
32ms

配列のlengthを退避して実行した場合

Firefox3.0
7ms
IE6.0
16ms
IE7.0
31ms
Safari3.1
16ms
Opera9.5
16ms


おお~、全然違うみたい。
とはいっても9万回以上ループした場合の結果なので、実際の開発でこれぐらいの差がでるかどうかは微妙ですが、塵も積もれば何たらってやつで意識しておけば処理の重いJavaScriptでもちょっとしたメリットはあるかもしれません。

■関連リンク
JavaScriptの配列をも~っと深く理解する:lengthの不思議な動作 - builder by ZDNet Japan
配列を for で回すときは length を何度も参照すると遅い - gan2 の Ruby 勉強日記

Webサイトの高速化 - styleはheadタグ内に書くと描画が速い

Webサイトの高速化 ルール5 CSSは上に! (Yahoo! developer netoworkより翻訳) | パフォーマンスチューニングblog | インターオフィスより

スタイルシートの読み込みは普通headタグ内で行いますが、ときとして後からHTMLに追記したスタイルがHTMLの下のほうにある場合があります。
(本当はちゃんと別ファイルとして作成しなきゃいけないのですが、直接書かれている場合があったり・・・)

こういったことをしてしまうと、画面の描画に影響を受けるということを以下の記事で知りました。
IEなどはスタイルが読み込み終わるのをじ~と待っていて、Firefoxは待たずにどんどん描画してしまうので、display:noneな要素も一瞬見えてしまうなどの問題があったりするみたいです。



スタイルシートを下のほうに配置すると、IEも含めて多くのブラウザにとって、徐々にページを表示することができなくなります。ブラウザは、スタイルが変更されてページ要素を再描画しないといけなくなるのを避けるために、ページを読み込み終わるまで描画をブロックします。そのため、ユーザは真っ白なページを見続けることになります。Firefoxは描画をブロックしません。そのためスタイルシートをロードし終わった時にページ要素を再描画する場合があります。その結果、the flash of unstyled content problem(スタイル適用前のコンテンツが一瞬見えてしまうこと)といった問題が発生する場合があります。


via: Webサイトの高速化 ルール5 CSSは上に! (Yahoo! developer netoworkより翻訳) | パフォーマンスチューニングblog | インターオフィス


ユーザビリティとして真っ白な画面が何秒か表示されているのは具合が悪いもんです。
たとえば昔、巨大なTableタグでHTMLを囲っているときがありました。
今みたいにdivタグでfloatされるのが流行る前ですね。
Tableタグはその中身がダウンロードし終わらないと描画が開始されないので、ブログとかのテンプレでこの方法を使っているとユーザーは5秒以上何もない画面を見たりしてました。

それとAjax関連で通信をしているあいだ、グルグル回るアニメーションgifとかで「今検索してますよ~」を通知して待っているあいだもユーザーを不安にさせないなんて方法も今となっては主流ですね。

ちょっとした工夫で描画が変わってくるのはなかなか実感がないですが、意識しているだけで全然違うんでしょう!

いやはや勉強になります。

キングボンビーがサイトを侵略する?jQuery Plugin - kingbonbi.js作りました

更新履歴

2008/08/23
- いろいろ調整しながらなので、動かなかったりご迷惑をお掛けいたします。とりあえず落ち着きました。
- ブログパーツ用のコードを変更しました。JSファイルの読み込みではなくアンカータグにしました。

kingbonbi
なんかものが落ちるjQuery Plugin - JDropper作りましたに引き続きオモロ〜なJavaScriptを作ってみました。

なんでキングボンビーを採用したのかよく分からないのですが、お風呂の中でなんか面白いことJavaScriptで出来ないかな〜と考えていたらキングボンビーに行きつきましたw
しかも名付けて「kingbonbi.js」!!
そのままです。

ブログパーツかBookmarkletとして公開いたします。
また今回もソースコードは公開いたしますので、何かの参考になれば幸いです。

続きからご覧ください。

Continue reading

jDropper.jsのブログパーツとBookmarklet

更新履歴

2008/08/23
- ブログパーツ用のコードを変更しました。JSファイルの読み込みではなくアンカータグにしました。

この前リリースしたなんかものが落ちるjQuery Plugin - JDropperのブログパーツとBookmarkletを用意してみました。

単になんかものが落ちるだけのjQuery Pluginですが、いろんなブログ上で実行してみると背景の配色が各々のブログで違うのでなんか面白かったですw

ブログパーツ用のコード

<a href="javascript:(function(){var%20s=document.createElement('scr'+'ipt');s.charset='UTF-8';s.language='javascr'+'ipt';s.type='text/javascr'+'ipt';var%20d=new%20Date;s.src='http://lab.hisasann.com/jdropper/js/bookmarklet.js?u='+escape(document.location.href)+'&d='+d.getMilliseconds();document.body.appendChild(s)})();"><img src='http://lab.hisasann.com/jdropper/img/jdropper2.png' style='border: 0px ;'/></a>

Bookmarklet用のリンク

以下のリンクをブラウザのお気に入りにドラッグしてください
jDropper

ご利用にあたって

本PluginはjQueryを使っておりますので、ブログパーツやBookmarkletを使った場合に画面の挙動がおかしいなどの現象がある場合にはご利用にならないようお願いいたします。

jQueryのheight()やwidth()はOpera9.5に対応していない気がする

jQueryは1.2からheightメソッドやwidthメソッドでdocumentwindowのサイズを取得することができるが、Opera9.5対応がされてないような気がしたのでメモメモ。

heightメソッドやwidthメソッドは本当に便利で以下のようにエレメントの幅や高さを簡単に取得することができる。

これはこれで重宝するメソッドではあるのですが、どうもOpera9.5でwindow(表示されている領域)の幅や高さを取得したときに思ったとおりにならなかった。

で、実際jQueryを見に行ってみた。

jQuery 1342行目

No~~~~~!
Operaの場合(jQuery.browser.opera)にdocument.body[ "client" + name ]で取得している。
このdocument.body[ "client" + name ]はおそらくOpera9.5未満のバージョンで有効なんじゃないかな?
今手元にその環境がないので確かめられないのだが、以下のとおり9.5からは
document.documentElement.clientWidthdocument.documentElement.clientHeightを使うべきだと思う。

Opera 9
標準
document.body.clientWidth
document.body.clientHeight


互換
document.body.clientWidth
document.body.clientHeight


Opera 9.5
標準
document.documentElement.clientWidth
document.documentElement.clientHeight


互換
document.body.clientWidth
document.body.clientHeight


via: ブラウザの表示領域のサイズを取得する方法。 - Enjoy*Study

ということでOperaでうまく取得できないので、自作!

jQueryのメソッドをさらにラップする形にしてみました。
これでいけるっしょ!
でもこれだと今度9.5にしか対応していないから、バージョンごとに分けないといけないのか。
面倒だな〜w

次のjQueryのバージョンで直るのかな?

Safariでは画像のloadが終わっていないとwidth、heightがうまく取得できない

IE6.0、IE7.0、Opera9.5、Firefox3.0ではちゃんと画像の幅や高さが表示されるのに、
Safariでは画像のwidth属性height属性をキチっと指定しないと「0」になってしまう。

今回はjQueryを使ってサンプルを紹介していきます。

たとえば以下の場合

そんなに普段画像の幅や高さを取得したりすることは少ないかもしれないが、
意外とハマるので気をつけたい。


そしてこれを回避するためには以下のように画像のonloadを待つ必要がある。

これでSafariでようやく画像の幅、高さが取得できるが、毎回onloadイベントをbindさせるのも面倒だし、
バインドできないシチュエーションなんかもあるかもしれない。

なので単純にimgタグの中にwidth属性とheight属性を必ず書くようにする。
これでSafariの場合でも問題なく幅、高さを取得できます。

実際のデザインのときはだいたいimgタグのwidth属性とheight属性は書いているけど、
書き忘れ時にこの現象に遭遇するでしょう。
こうゆうのって意外とハマりますよね。

なんかものが落ちるjQuery Plugin - JDropper作りました

更新履歴

2008/08/21
- easingの種類を増やしました
- id:pyangoroさんの指摘で名前を変更しましたw
- id:ぁゃιくない(*~Д~)さんのさんまの名探偵的なゲームだと面白いという意見によりちょいゲーム化
- duration(速度)をパラメータとして渡せるようにしました

jdropper

画像やHTML要素が雨のように降り注いだら面白いな〜と思ってこのjDropperを制作しました。
特にこれを使って何かが出来るわけではないですが、ブログパーツの一種として使っていただけたら幸いです。

続きからご覧ください。