goog.dom.getElementsByTagNameAndClassは配列を返さない
タグ名やクラス名を指定してエレメントを取得する基本的なメソッドです。
これは極力ネイティブのquerySelectorAllやgetElementsByClassNameを使おうとするので、FireFoxやIE8ではなかなか高速に動きます。
しかしgetElementsByClassNameやgetElementsByTagNameは生きたNodeList(HTMLCollection)を返すので、配列と同じように扱っていると偶に痛い目に会います。(getElementsByClassName()の落とし穴)
※querySelectorAllは静的なNodeListを返すそうです。動的だと思ってました。(querySelectorAllがliveじゃないNodeList返すのはなんで?)
ここまでは別にClosure Library固有の問題ではないのでご存知の方も多いかと思われます。
ではquerySelectorAllもgetElementsByClassNameも実装されていないIE6やIE7でgoog.dom.getElementsByTagNameAndClassがどう動くかというと、こんなコードになっています。
var els = parent.getElementsByTagName(tagName || '*');
if (opt_class) {
var arrayLike = {};
var len = 0;
for (var i = 0, el; el = els[i]; i++) {
var className = el.className;
// Check if className has a split function since SVG className does not.
if (typeof className.split == 'function' &&
goog.array.contains(className.split(/\s+/), opt_class)) {
arrayLike[len++] = el;
}
}
arrayLike.length = len;
return arrayLike;
} else {
return els;
}
arrayLike変数は実質配列なのにObject型のインスタンスになっています。
これにはgoogleの「クロスブラウザ対応時に余計なバグを埋め込まないよう、絶対にArrayは返さないぞ」という心遣い(?)を感じます。
しかしそんな心遣い(?)も虚しく、生きたNodeListと死んだものとが混在することはすぐに罠となります。
例えば特定のエレメントを削除したくて
var els = goog.dom.getElementsByTagNameAndClass('input', 'text' element);と書いた場合、生きたNodeListが返れば歯抜けになるでしょう。だからといって
for (var i = 0, el; el = els[i]; i++) {
goog.dom.removeNode(el);
}
var els = goog.dom.getElementsByTagNameAndClass('input', 'text' element);と書くと、死んだNodeListで無限ループ or エラーになるでしょう。
var el;
while ((el = els[0])) {
goog.dom.removeNode(el);
}
しょうがないので私は
var els = goog.dom.getElementsByTagNameAndClass('input', 'text' element);と書きました。
for (var i = els.length -1, el; el = els[i]; i--) {
goog.dom.removeNode(el);
}
ちなみにDelphiにはこんなこともあろうかとfor-downto-doという構文があります。どうだ、羨ましかろう。
パフォーマンスのためとはいえこんな基本的な部分にブラウザ依存な罠があることにゾッとします。
何かベストプラクティスはないのでしょうか?
- Category(s)
- JavaScript
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/uchida/goog-dom-getelementsbytagnameandclass306f914d521730928fd43055306a3044/tbping
Re:goog.dom.getElementsByTagNameAndClassは配列を返さない
ではどうでしょうか?