javascript

Entries

Singleton Pattern in Javascript

Date
2007-08-23 (Thu)
Category
javascript

突然思いついたので、メモ。Google で検索した限り、こういう書き方をしている人はまだ誰もいないはず…どうかしらん。ちなみに meme は même のことで、フランス語で self / same の意味です。

var Singleton = function ()
{
 var err  = new Error('This is Singleton pattern. Please use
getInstance() instead');
 err.name = 'SingletonError';
 throw err;
}

Singleton.meme = Singleton;
Singleton.initialize = function() { var instance = { 'code': 'implementation comes here' }; this.meme.getInstance = function (){ return instance; }; delete this.meme.initialize; }
Singleton.getInstance = function () { this.meme.initialize(); return this.meme.getInstance(); }

try { s = new Singleton(); } catch (ex) { console.log(ex); }

try { s = Singleton.getInstance(); console.log(s); } catch (ex) { console.log(ex); }

ま〜、結局根が Dynamic で、アクセス制限も微妙な javascript なんで、Singleton パターンがどれほど重要か、という議論はあると思いますが。この考え方を使えば Singleton などを強制できるライブラリとか作れますね。

きっかけは、JavaScript Coding Guidelines for Mac OS X: Memory and Performance Considerations でした。

Release initialization functions. Code that’s called once and never used again can be deleted after its execution. For instance, deleting a window’s onload handler function releases any memory associated with the function, like this:
var foo = function()
{
    // code that makes this function work
    delete foo;
}
window.addEventListener('load', foo, false);

Web で使ってる限りでは、普通こういう事はしないよなぁ、と思いました。いやもうちょっと正確に言うと、Web で使ってる限り、数分単位以上 走り続ける Javascript なんてあんまり考えないよなぁ。Dashboard は、数日単位で走り続ける可能性があり、そのためにはこれくらいしないと行けないんですね。

これで思い出したのは、イントラネットで使うカレンダーの設計。全てを Javascript で実装しましたが、いちいち Event を delete しないと、オブジェクトへの参照が消えないので、数回の読み込みでメモリがパンクする、という事を発見したことがありました。

オチなしですが、最近 blog ご無沙汰だったので。

Google Gears を使って Game of Life を作る: その2 - Workerpool について

Date
2007-07-08 (Sun)
Category
Google | javascript

暫く間があきました。6月の後半から先週まで、プロジェクト3つが並列に走っていたので、ちょっと時間が取れませんでした。忘れてしまう前に Google Gears の Workerpool について少しまとめておきたいと思います。

  • 以前のエントリにも書きましたが、PC のメモリが無限でないように、Workerpool も無限に作る事は出来ません。
  • エラーがあるときは、基本的に無言で Firefox ごと落ちます。それが精神的にも良くないし、問題の特定を難しくしていますが、理不尽な仕様やバグなど、僕はまだ聞いていません。つまり無言で落ちるような状況では、おそらく自身のコードを疑うべきでしょう。
  • Worker が他の object とメッセージ交換する方法は、worker の id を指定してメッセージを送るだけ、それのみです。String や Number 以外のものをやり取りしようと思ったら、Object を JSON にシリアライズして、それを eval するしかないようです。ここでメモリが共有できたりすればスピードアップでも出来そうだけれども。
  • Worker を作る時に、その Worker がすべき作業書のようなものをスクリプトとして渡さなければいけません。mycom の "重たいJavaScript処理"もこれで解決! - Google Gearsのワーカプールを試す や本家 Google の Google Gears WorkerPool Demo では オブジェクトをStringのコンストラクタとして渡して、JSON シリアライズをやっていますが、まず僕にとってその方法が直感的でないし、将来 Safari なんかで動くようになった時にそれでは動かないでしょう。それにメインで使わない関数で object の名前空間を汚染するのもやだし。 そこで別の方法を考えました。Here Document が使えれば一番いいんだけれどもブラウザ上の Javascript 実装にはそんな機能は存在しません。結局 HTML の pre タグ内にコードを書き込んでおいて、それを取得する、という形にしました。その時ただ読み込むだけだと、Browser が勝手に挿入する WBR 要素と、大なり/小なり記号が実体参照に変換されてしまうのでそれだけ変換するようにしました。コードは以下。
    Union.prototype.initScript = function (elm_id) {
      // workscript holder should be <pre> tag or
      // you have to set white-space: pre on CSS property.
      var elm_pre = $(elm_id);
      var script  = "";
      var len     = elm_pre.childNodes.length;
      for (var i = 0; i < len; ++i) {
        switch (elm_pre.childNodes[i].nodeName) {
          case '#text':
            script += elm_pre.childNodes[i].nodeValue;
          case 'WBR':
          default:
            // do nothing;
        }
      }
      return script.replace(new RegExp(/\&lt\;/), '<').replace(new RegExp(/\&gt\;/), '>'); 
    }
  • Main Thread から Worker にメッセージを送る場合、Workerpool オブジェクトのインスタンスを使っての sendMessage メソッドを使ってメッセージを送ります。
  • 逆に各 Worker から、メッセージを送り返す場合は google.gears.workerPool.sendMessage という static メソッドを使います。しかしこの static method は Firefox の DOM Inspector には出てきません。あと、Google の例では gearsWorkerPool.sendMessage という別のメソッドを使っています。興味深いですね…

そんな所でしょうか。あとまた気がついたら追記します。

Google Gears を使って Game of Life を作る: その1

Date
2007-06-18 (Mon)
Category
javascript

Google Gears の一つ、WorkerPool を使って、Conway の Game of Life を作ってみました。

Conway's Game of Life by WorkerPool

Google Gears を使っているので、もちろん Firefox オンリーです。あと Google Gears のインストールもしていないと見られません。僕自身が Windows IE を持っていないので、わかりませんが、IE でも動くとは思います。メモリの搭載量が少ないコンピュータでの動作はお勧めしません。あと動かしてしばらくすると、Unresponsive Script っていう警告が出ます。以下のページによると…

Fix Firefox unresponsive script warnings

Open Location -> about:config -> dom.max_script_run_time

の値を変えてやると良いようです。僕は 200 くらいにしてありますが、警告自体は有用だと思うので、ページを見終わったらデフォルト(僕の場合は 10 でした)に戻しておくといいと思います。


Conway's Game of Life は僕が ITP で作った最初の作品でした。もともとは Macromedia Director の Lingo Script で書き、最大3人まで同時に遊ぶ事が出来て、参加者はネットワーク越しに盤面を共有できて、チャットができたり、ゲームの動作を協調的に管理できて、セルが死ぬ時にフェードアウトしていく、というオリジナルにない工夫をこらしたものでした。当時僕の英語はまったくダメだったので、説明はほとんど出来ませんでしたが。

今回作ったのは、まず WorkerPool という javascript の Multi-process というか Multi-thread に興味があったのと、まぁ懐古趣味でしょう。昔よりいいものがかけるかな、っていう期待も少しあり。

実際には、WorkerPool は少し癖が強い、というか何か一つ間違ってると途端に Host (Firefox) を道連れにして落ちます。 なので、エラーがなんで起きてるのかとかさっぱりわからず途方に暮れますね。前のエントリに書いたように、120個以上 createWorker して落としたり、単純なスペルミスで落ちたり、まぁいろいろありました。

今日はもう遅いのでとりあえずこれにて。次回以降、暫く WorkerPool の解説でもするかもしれません。あと Game of Life 自体ももう少し改良して、スピードの差などが図れれば面白いと思っています。

Google Gears: Worker Pool は 120個以上 createWorker できない

Date
2007-06-13 (Wed)
Category
javascript

Google Gears を触っています。いくつか覚え書き。

WorkerPool class の createWorker メソッドは worker ID を戻り値に持ちます。その worker ID は“guaranteed to be unique values that are never reused within the same WorkerPool” とありますが、今の所の実装では、0 から始まる int で、119 に達すると、次に workerpool 関連のメソッドが呼ばれたらエラーを発生させ、時に無言で Firefox ごと異常終了するようです。

あと 同じく createWorker メソッドなどで、引き数の設定などが正しく行われていないと(child から parent への messaging ハンドラがないとか)、やはり無言で Firefox ごと異常終了するようです。

まぁ Beta 版ですからなんとも云えません。しかし Worker は thread というよりむしろ process と似たような振る舞い、と reference にもある以上、数に制限は必ずあるのでしょう。となると Multiton で生成、いや処理を細切りにする事自体が間違ってるのかな?UI を止めないで重たいプロセスを走らせられる、というのはスゴいけれど、僕としてはもともとそれほど重たくない処理を分散化して超高速化!見たいなのを考えていたのだけれど…

これらは全て ITP で最初にやったプロジェクトを Google Gears の上に再現してみようと思って発見しました。コードはプロジェクトが出来た時に公開したいと思います。




あと話はすこし変わるけれど、Google Gears と Adobe Air (aka Apollo) を対抗技術とするのは違うと思う。Google Gears は Safari もそのうち にもある通り Safari での動作を視野に入れている、というか開発が進んでいる。Adobe は Air の基盤技術として webkit を使っているのだから普通に考えて、Adobe Air でも Google Gears は動くようになるのではないか(いや、まぁ webkit は実際主に HTML Rendering Engine だろう、Safari の plugin 機構は webkit とは別、という突っ込みはアリだ。しかし Javascript Engine の KJS は webkit に含まれているし、もともと Google Gears だって、プラットフォーム毎に内側で使っているものは違う)。というか Adobe は動かしたいだろう。Air Beta についてきた DB は、もともと Flex で外部に頼らず DB を叩きたい、とかそういった要望に応えるもので、Google Gears の SQLite とは大分経路が違うような(このへんは追っかけてないのであやふやかも)

そうなると旗色が悪くなるのは、もともとの対抗技術である Joyent SlingshotMicrosoft Silverlight やら Sun JavaFX のような気がする。Silverlight はそりゃ Microsoft のする事だからすぐに廃れる事はないだろうけれども… JavaFX とか誰か使う奴いるのかね?Annotation の駆使とか野心的なのはわかったけど、なに Operation って?昔 同じ関数でも戻り値をもつか持たないかで呼び方が違う言語があってそれってユーザフレンドリーじゃないな、って思った事を思い出した。Slingshot はあるいは風前の灯か、MDM Zinc の二の舞か(っつぅか AIR が Zinc のパクリだが、マーケティング能力に勝る Adobe には適うはずもなく)。

javascriptで生成されたIMG要素のブラウザに依る挙動の違い

Date
2006-08-18 (Fri)
Category
javascript

難しくないと思っていたのだけど、結構面倒くさい問題に取り組んでいることにようやく気づいた。

IMG element loading timing

Gallery みたいなサイトを作ろうとしている。画像は javascript に file path を配列で渡して、window.onload イヴェントで要素を追加していく。画像ファイルの大きさによって、挙動をかえるつもりだったのだが、Safari だけ画像の大きさをちゃんと読んでくれない。

正確には例えば <img src="***" id="testimg" /> みたいな要素を生成し document.body (かそれ以降)に挿入する。Firefox, Opera だと、その要素に width / height 属性を振っていなくても、$('testimg').width みたいなアクセスができるけど、Safari だと 0 が帰ってくる。

悩んだ後、document.images 配列だと Safari でも、width / height の属性値が自動的に補完されるようなので、そっちを使おう、と。で、一番上の問題というか、実装に気がついたというわけです。

  • Firefox は、img が生成された瞬間に、src 属性を参照しにいって、メモリに溜め込んでいます。
  • Opera, Safari, IE は img に view がついた時に src 属性を参照するようです。

って常識なんでしょうか。ちなみに Opera は自動補完される width / height 属性の値が間違っている時がありました。が、再現性が低いので、とりあえず保留にしておきます。

javascript版 trim と path 処理関連

Date
2006-08-17 (Thu)
Category
javascript

よく使う trim は String を prototype で拡張しました。ので、同名で違うことをしている人は、注意。path 関連は php に強く影響を受けています。というかもっときれいに書き直したいので、obsolete になる可能性大ですが、とりあえず挙げておきます。

使い方としては、

p = Path("http://umbra.nascom.nasa.gov/eit/images/eclipse/williams/Williams_College_wl.jpg")
alert(p["domain"]) // umbra.nascom.nasa.gov と表示される。 alert(p["dirname"]) // /eit/images/eclipse/williams と表示される。 alert(p["basename"]) // Williams_College_wl.jpg と表示される。 alert(p["extension"]) // jpg と表示される。

こんな感じ。Path() の戻り値は Object ではなくて Array です。Associated Array というか Hash なので、prototype.js でほにゃほにゃしたい時は例えば、

p = $H(Path("http://umbra.nascom.nasa.gov/eit/images/eclipse/williams/Williams_College_wl.jpg"));
p.each(function(el){
   alert(el)
});

とかすると

domain, umbra.nascom.nasa.gov
dirname, /eit/images/eclipse/williams
basename, Williams_College_wl.jpg
extension, jpg

みたいになります。

Source は以下。

Continue reading

Google Reader to livedoor Reader

Date
2006-05-10 (Wed)
Category
Web | javascript

Project も終わったことだし(嘘)、そろそろ livedoor Reader も試しとかないとな〜ということで、またまた OPML の季節がやって参りました。Google Reader をメインにしている人用の OPML export 術です。Bloglines を使っている人は、過去エントリ Google Reader! を参照のこと。先に云っておくと、ここで紹介する方法は全く techy ではないので、web でメールが書ける人なら誰でもできると思う。

Google Reader の場合はめちゃ簡単。Google Account にログインした状態で以下のリンクをクリック。

http://www.google.com/reader/subscriptions/export

以上。Safari だったらソースをみて保存するなり、Firefox だったらそのままページを保存するなり、ローカルファイルにして、(ファイル名は opml.xml にした方がいいかも)、livedoor Reader の“設定変更”→“データのインポート”で、Lightbox 風のウィンドウがでてきて、インポート完了。Google Reader のタグ設定がそのままフォルダに分類されます。


ちなみに上述の URL は javascript object をいじっていて見つけましたが、Official な機能として実装されています。普通は、

edit subscription {tab} -> more actions {drop down menu} -> Export subscriptions

で行けます。それにしても今回の taggit.in プロジェクトで、javascript の読み方は異常に理解したので、Google のコード読むの面白い。Object detection コードとかそのまま使えそうなのもいくつかありました。追々紹介します。

JSON

Date
2006-05-06 (Sat)
Category
Web | javascript | php

しばらく開いてしまいました。来週、ITP の Spring Show にて、Makiko Saito 氏、Sonali Sridhar氏と共同で、taggit.in というプロジェクトを出展します。平日夜ですが、お時間のある方は是非。僕もいます。その中から、いろいろ書いておきたいことはあるのですが、詳しくは発表後

製作メモとして、今回は JSON の話。

今回のプロジェクトは、いろんなページから、taggit.in にアクセスして、データを引き出す、というのものなので、AJAX の XMLHttpRequest は domain 制限により使えません。という訳で、data は JSON にして受け渡しています。サーバ側では、Hawk 氏の JSONEncoder を使って、クライアント側では、本家にある詠み人知らずの JSON.js を活用させてもらっています。

そこで一つ問題があったのが、Single Quote (x27) の扱い。仕様上 Single quote は escape しなくていい感じですが、実際しないと困ります。例えば…

var container = JSON.parse('{"a":"hello","b":"My Uncle's house."}');

parse は String を受け取るから、Single Quote か Double Quote でくくる必要があるし、Double Quote は JSON で使われてるし…結局、少し JSONEncoder 改造しました。というか、スゴい簡単だった。69行目 前後に始まる “ $this->_transTable = array( ” に "\x27" => "\'" を一つ追加しただけ。array 全体を以下に。

$this->_transTable = array(
	"\x08" => '\b',
	"\x09" => '\t',
	"\x0A" => '\n',
	"\x0C" => '\f',
	"\x0D" => '\r',
	"\x22" => '\"',
	"\x27" => "\'",
	"\x2F" => '\/',
	"\x5C" => '\\\\'
);

何か僕の知らない深遠な理由があるのかな? Single Quote を Escape しないのには…

Accordion Effects by Script.aculo.us

Date
2006-04-05 (Wed)
Category
javascript

Demo

Try
Accordion Effect.

Download

download:
Accordion 1.0
license:
MIT License, same as script.aculo.us

What is this?

On Rico site, it shows as Accordion Exmple, and you can see the top page of Moo.fx (it's called fx.Accordion in their Documentation page) as well. I made a similar effect only with Script.aculo.us. You can try its working demo from the link on the top. In short, this is a way to show several panels, which you can only see ONE panel at one time, and choose the one you see by clicking on it.

There is an implementaion on Wiki of Script.aculo.us, but it seems like no scalability at all (put unique id on each panel? NO WAY!), I made by myself for my purpose.

How to use it?

Of course, you need to have prototype.js and scriptaculous.js(?load=effects). After loading these two, you can do like..

<script type="text/javascript" src="accordion.js"></script>

in order to load the script.

And then, here is an example of Accordion-behavior HTML elements.

<div id="accordion">
<div class="panel"> <h3>Item 1</h3> <div class="panelBody"> <p>Something something.</p> </div> </div>
<div class="panel"> .... </div>

Here comes a list of spec.

  • “Accordion” must be marked-up by a Block element with unique ID.
  • Each panel must be marked-up by a Block element with class name of “panel”
  • Each panel must have a heading element (doesn' have to be HTML's HEADING element, such as H1, H2.. in terms of meaningful mark-up, it is strongly recommended though)
  • Each panel must have a body element, and be given a class name of “panelBody”.

Finally, you can instantiate the class, like following.

var acc = new Accordion("accordion", "h3", "acc");

Accrodion class (basically) accepts 3 arguments.

  1. Id name of entire Accordion elements
  2. Tag name of each panel's heading element
  3. Instance name (technically, it is just a variable name in global context, which points to Accordion Object )

Other

You can actually change the class name of Panel Element (“panel” as default) and Panel Body Element (“panelBody” as default). When you instantiate Accordion Object, you can put customized a class name of Panel Element as a fourth argument, and Panel Body Element as a fifth.

Enjoy!!

Script.aculo.us を使った Accordion Effect(完成)

Date
2006-04-05 (Wed)
Category
javascript

昨日悩んでいた奴ができました。

デモ

Accordion を試す

ダウンロード

ライセンスは… script.aculo.us と一緒で MIT License にでもしておきます。

Accordion ja 1.0

これは何?

Rico のページで Accordion Exmple として紹介されていたり、Moo.fx でも Top ページで使われていて、Documentaion ページでは fx.Accordion として紹介されているようなことを、Script.aculo.us だけを使ってやってみようというものです。上のサンプルをみてもらった方がわかりやすいと思いますが、いくつか並んだパネルの中、クリックして表示部を選択できますが、開いているパネルは常に一つ、というもの。

Script.aculo.us でも Wiki にひとつ実装が載っていますが、汎用性が低そうだったので(項目ごとに id なんか打てるか!っつぅの)、僕が使いやすいように実装してみました。

使い方

まず、prototype.js と scriptaculous.js(?load=effects) は必須です。二つを読み込んだ後に、

<script type="text/javascript" src="accordion.js"></script>

として本体スクリプトをロードしてください。

次に実際に Accordion 動作をする HTML 要素ですが、こんな感じです。

<div id="accordion">
<div class="panel"> <h3>Item 1</h3> <div class="panelBody"> <p>Something something.</p> </div> </div>
<div class="panel"> .... </div>

満たすべき条件は以下。

  • Accordion は任意 id の block 要素で囲われていること
  • 各パネルは “panel” という class nameの block 要素で囲われていること
  • 各パネル毎にヘッダ要素(畳まれた状態でも視認可能な要素)を一つ設定すること
  • 各パネル毎にボディ要素(開いた時に視認確認になる要素)を一つ設定し、“panelBody” という class nameを与えること

そして以下のように初期化すると蛇腹として動作するようになります。

var acc = new Accordion("accordion", "h3", "acc");

Accordion の引数は以下の3つ

  1. Accordion 全体を囲う block 要素の id
  2. 各パネルのヘッダ要素を構成する Tag 名
  3. 変数(インスタンス)名

その他

パネル要素クラス名(default では“panel”)とパネルボディ要素クラス名(default では“panelBody”)は変更可能です。Accordion クラス初期化の際に 第四引数としてカスタムパネル要素クラス名、 第五引数としてカスタムパネルボディ要素クラス名が指定できます。

今回の実装で気がついたことなどは次回エントリで。。

Script.aculo.us を使った Accordion Effect

Date
2006-04-04 (Tue)
Category
javascript

Script.aculo.us の このページ とか moo.fx のトップページ なんかに使われてる、クリックすると、その場所が開いて、他の場所が閉じる、みたいな Effect。Scriptaculous で、きれいに書いてみようと思ってやっています。

Apr. 4, 2006版

実は、最後の部分で行き詰まっています。h3 の onclick handler 内では、実行コンテキストがかわって、呼び出し元オブジェクトを参照できない。。

しかも Safari でみると、エラーは全く出ずに、ただ、動きませんね。なぜ?とりあえず up して、しばらく頭を冷やします。。。

追記(Apr 5 2006):できました。詳しくは次のエントリにて。

&ldquo;var is&rdquo; はまだ使える。

Date
2006-03-23 (Thu)
Category
javascript

slashdot のIE7はWindows Explorerと分離へを読んで、この間 var isなんてエントリを書いたばっかりだけど、使えなくなんのかよ!と焦った。

Internet Explorer との互換性向上
サイト互換性のため、判別を含まない document.all サポートが追加されました。また、キーボードアクセラレータの互換性を改善したことで、Internet Explorer ユーザがよりスムーズに移行できるようになりました。
Mozilla Firefox Preview Release (Greenlane) リリースノート

しかりよく見たらリリースノート古いしさ。焦ってググリまくったら、すご〜い古い話題だと気がついた。全ての正解は以下。

【DOCTYPE指定のXHTML文書】
 document.all      = undefine
 document.layers     = undefine
 if(document.all)    = false
 if(document.layers)   = false

【DOCTYPE指定なしHTML文書=quirks モード】
 document.all           = [object HTML document.all class]
 document.layers          = undefine
 if(document.all)         = false
 if(document.all('IDname'))    = true
 if(document.layers)        = false
 document.all("idname")      = [object HTML DivElement]
 document.all("idname").offsetTop = 正しい値
  
 document.allを使用すると、以下の警告がJavaScriptコンソールに出ます。
   →Warning: Non-standard document.all property was used.
         Use W3C standard document.getElementById() instead.
Mozのdocument.allサポート経由で見つけたMozilla が何と doument.all をサポートした!!

つまり standard 準拠モードなら var is は使える、と言うことですね。

検証するには、例えば

以下の bookmarklet をこのページで使うと、(IE 以外では)エラーが出ます。

document.all('container')

でも google のトップページに行って

document.all('1a')
document.getElementById('1a')

同じダイアログが現れたのではないでしょうか。ちょっと焦ったけど、ウラ取りが大事な世界に僕らは居るんだなと感じた今日この頃でした。

Bookmarklet 用 Dynamic Script Loader

Date
2006-03-06 (Mon)
Category
javascript

これの続きと言えば続き。Bookmarklet で、任意のページに script 要素を挿入する時、複数ファイルを挿入するとして、それらのファイル同士に依存関係があった場合、読み込みタイミングのせいで、うまく行かないことがあります。

例えば prototype.js と scriptaculous をある bookmarklet で使いたくて、両方読み込むようにするとしても、普通に var s = document.createElement("script");... というのを3個連続させると、何々が定義されてないとか出るわけです。やってみましょう。

試しに振ってみる

まずは prototype.js を読み込んで、変形版 scriptaculous を読み込み、それでそれら両方を使った javascript を実行させています。おそらく一回目にクリックしたときは、コンソールにエラーが出ます。2回目にクリックすると、エントリ全体が揺れる、はず。

しかしネットワーク状況もあるだろうし、動作は一定ではないみたいですね。でも作る側としてはエラーはイヤじゃないですか。と言うわけで作ったのがこの逐次順列読み込みscript。

DySL.js

この object を使えば、読み込む script の順番を完全に制御できます。使い方はこんな感じ。

var dysl = new DySL();
dysl.load(
	"http://tksh.local/beta/alert2.js", 
	{src:"http://tksh.local/beta/alert3.js", id:"abcdedf"}, 
	"http://tksh.local/beta/alert4.js"
);

load メソッドは、src attribute に当たる文字列か、src というラベルを持つ Object を受け付けます。script が持ちうる attribute なら何でもラベルにくっつけてやれば(language とか charset とか)、そのまま反映されます。

欠点としては、読み込まれる側の script に、必ず手を加えなければいけないことでしょうか。ま、是は bookmarklet での使用があるので、多少の手入れは容認しました。ツールを作ればいいのかな。。

Continue reading

Objection Detection: var is

Date
2006-03-02 (Thu)
Category
Web | javascript

前エントリ中で使ったのですが、browser detection のお話です。正確には Object Detection と呼ばれる技。

event の設定には prototype.js 内の Event.observe(...) などが、うまく抽象化されていて使いやすいんですが、設定する event handler が共通ではないとうまく行きません。今、僕のしたいことは IE vs Modern browsers で共通の event handler を見つけることは出来なさそうだったので、切り分けのコードが必要です。

var is という javascript を最初に見たのはありみかさんのあれこれ popup だったような気がするんですが、現行コードには入って無いみたいですね。というわけで調べてみました。

Quirksmode: Object detection

眠いんで説明しませんが理由付けがしっかりされています。

JavaScript Kit: Determining browser type using object detection

違う browser で使える object の表があります。

僕が今回必要なのは IE か Modern Browser(Firefox / Safari / Opera)かそうでないか、くらいなので、こんな感じでした。

var is = {
ie : document.all && !window.opera,
modern : document.getElementById && !document.all,
opera: window.opera && document.getElementById
}

try: document.all
try: document.getElementById
try: window.opera

getElementById だけだと IE 5 以上は true を返しそうです。Opera には document.all があるっぽいから、別途分けなければイケません。なので使う時には…

if (is.modern || is.opera) {
// do something for modern browsers.
} else if (is.ie) {
// do something for modern browsers.
}

こんな感じでしょうか。

DOMNodeInserted

Date
2006-03-01 (Wed)
Category
Web | javascript

僕のよくある悪い癖なのだが、思い付いたことを実現しようといろいろ調査をしていると、目的を実現できそうな切っ掛けとなる技術を発見する。本当にそれでいけんのかいな、とさらに深い調査を始めると、最初に考えていた目的を忘れて、結局その最初に見つけた技術で出来ることを全て試すことになって、あぁ { 面白かった | つまんなかった }で、何してたんだっけ? みたいなことが多い。最悪なのはその結果を全て忘れて、同じことを繰り返したりすることなんだけど、結構やっちゃうんだよね…

と言うわけで今回もまさにそれ。忘れないように個々にメモします。

最近取り組んでいる bookmarklet を使った project 用に、script tag の読み込み完了をイヴェントハンドラに出来ないかどうか調べていました。以下結果。


script tag に onload を付ける。

ちゃんとした仕様書を見つけられた無かったけれどDOM では onload は限られたエレメントでしか fire しないようです。強いて言えばここ DOM-Level-2-Events にすこし載っています。

firefox
ファイル読み込み終了後 fire する
Opera 8.51
fire しない
Windows IE 6.0
fire しない
Safari 1.3.2
fire しない

と、見事に firefox 以外は fire しません。当たり前ですね。余談ですが、Firefox では img タグでも onload が fire します。となると、src attribute がつくタグは全て onload が fire するんじゃないかと期待できそうですが、それはそれで意味が通じます。特に“読み込み終了後 fire” というのは非常に通好みと言うか。以下、実験に使ったコード抜粋です。

<script type="text/javascript" onload="javascript:void(alert('onload'));" src="alert.js"></script>

alert.js には

alert("in script");

とだけあります。

onload sample

body に Listner Object的なものを付けてscript タグの挿入を監視

上でも黙って Quirksmode にリンクしましたけれども、javascript で event 周りをやろうと思ったら Quirksmode は素晴らしい文章の宝庫です。で、ずばり The events というページはすこからさき全ての文章への portal になっている、と言うか。以下のページ群は見ているだけでも楽しい様な browser で使用できる event ハンドラが列挙されています。(Opera 向けのページは何処にあるのだろう?)

Quirksmode の記述とやや異なりますが、Safari は DOM Level 2 Events を全て実装しているとあります。Web Specifications Supported in Opera 8 によれば Opera 8 も同様のようです。Free になったのは此のバージョンですから、まぁこれが主流になると考えて差し支えないでしょう。

と言うわけで、本題の DOMNodeInserted です。仕様書より引用すると、

Fired when a node has been added as a child of another node. This event is dispatched after the insertion has taken place. The target of this event is the node being inserted.

ですから、DOM Node を何処か別の DOM Node に挿入が完了した時に fire されるようですね。試してみましょう。以下のページにて。


DOM Node Inserted Sample

タイミングを計るのは難しいですが、上記 sample ページで読み込んでいるコードはしていることは同じですが、コメントを使ってファイルサイズを稼いでいます。やってもらえばわかりますが、DOMNodeInserted で fire される alert(MutationEvent と表示される)が出てから、alert2.js の alert (in script と表示される)が呼ばれます。(分かりにくい)

あとキモはこのコードでしょうか。

document.body.addEventListener('DOMNodeInserted', function(e){
alert(e);
}, false);

DOMNodeInserted で fire される function には デフォルトで MutationEvent という object が渡されます。その property を見れば、どんな DOM Node が挿入されたかわかる、という仕組みです。


が、Windows IE でこれを実現できる event handler を見つけられていないので…結局意味ないじゃん!みたいな。ただ listener object というアイデア自体はいけそうですね。

Return to Page Top