DOM 要素の集合のイテレーション
Update: id:reinyannyan:20060411:p1 に続きます
getElementsByTagName の様なメソッドで得られた、配列ではない要素集合を配列のように扱いたい時に、prototype.js では "$A" という関数によってまず配列化する、という方法を用います。
これは非常に素晴らしい、便利な方法なんですが、集合の規模が大きくなると、それだけ配列化によるオーバーヘッドも大きなものになってしまいます。
そこで、仮にこのようなものを作ってみました。
// DOM Iterator var Domi = { each: function (collection, yield) { for (var i = 0; i < collection.length; i++) yield(collection[i]); } };
べつに配列が欲しいわけじゃない、単純に each でイテレートしたいだけ、という場合に使える方法です:
Domi.each(document.getElementsByTagName('A'), function (a) { print(a.href); });
配列化のオーバーヘッドが無くなるという点で、結構高速化が見込めるんじゃないかと思います。
さらに、この each に対して用途に応じたメソッドを被せていくことで、Enumerable と同じような応用をすることもできます (何なら Enumerable を組み込んでも良いかも知れません)。
このことを踏まえて、次の記事: IT戦記『prototype.js の document.getElementsByClassName は重い』で指摘されている同様の問題について考えてみました。
根本的な解決にはなりませんが、each が出来さえすればよい、というケースに限れば以下のようなメソッドを作ることができるでしょう:
Domi.class_each = function (className, yield, parentElement) { var children = ($(parentElement) || document.body).getElementsByTagName('*'); // 正規表現はキャッシュしておく! var pattern = new RegExp("(?:^|\\s)" + className + "(?:\\s|$)"); this.each(children, function (child) { if (child.className.match(pattern)) // 1.5.0_pre1 (以降) の Element.extend は省略しています yield(child); }); };
個人的に DOM 要素を配列として保持したいと思うことがまず無いので、これで十分かなという感じです。