DOM検索効率化のメソッド群を作ってみた。
というわけで、もういっちょ。考えていたDOM検索効率を考えたメソッド群を作ってみた。
ええと、prototype.jsのElementオブジェクトにaddMethodsしていることからもわかるようにprototype.js必須っす。ええと、私の使っているprototype.jsは、1.5.1ね。script.aculo.us(v1.7.1_beta3)と一緒に使っているので。
-
document.getElementsByClassNameAndTagName = function(className, parentElement, tagName) {
-
if (Prototype.BrowserFeatures.XPath) {
-
return $(parentElement).getElementsByClassName(className);
-
} else {
-
var children = $(parentElement).getElementsByTagName(tagName || '*');
-
var elements = [], child;
-
for (var i = 0, length = children.length; i <length; i++) {
-
child = children[i];
-
if (Element.hasClassName(child, className))
-
elements.push(Element.extend(child));
-
}
-
return elements;
-
}
-
}
-
-
Element.addMethods({
-
getElementsByClassNameAndTagName: function(element, className, tagName) {
-
return document.getElementsByClassNameAndTagName(className, element, tagName);
-
},
-
-
nextElement: function(element) {
-
do {
-
element = element.nextSibling;
-
} while (element && element.nodeType != 1);
-
return $(element);
-
},
-
-
previousElement: function(element) {
-
do {
-
element = element.previousSibling;
-
} while (element && element.nodeType != 1);
-
return $(element);
-
},
-
-
firstChildElement: function(element) {
-
var child = element.firstChild;
-
while (child && child.nodeType != 1) {
-
child = child.nextSibling;
-
}
-
return $(child);
-
},
-
-
lastChildElement: function(element) {
-
var child = element.lastChild;
-
while (child && child.nodeType != 1) {
-
child = child.previousSibling;
-
}
-
return $(child);
-
},
-
-
childElements: function(element) {
-
var children = [];
-
var child = element.firstChild;
-
while (child) {
-
if (child.nodeType == 1) {
-
children.push($(child));
-
}
-
child = child.nextSibling;
-
}
-
return children;
-
},
-
-
childElement: function(element, index) {
-
var nodeIndex = 0;
-
var child = element.firstChild;
-
while (child) {
-
if (child.nodeType == 1 && index == nodeIndex++) {
-
return $(child);
-
}
-
child = child.nextSibling;
-
}
-
return null;
-
},
-
-
cleanWhitespaceRecursive: function(element) {
-
var f = function(element) {
-
var child = $(element).cleanWhitespace().firstChild;
-
while (child) {
-
if (child.nodeType == 1) {
-
f(child);
-
}
-
child = child.nextSibling;
-
}
-
};
-
f(element);
-
return element;
-
}
-
});
nextElement, previousElementはそのまんま。textNodeはすっ飛ばして、elementNodeだけを見ている。firstChildElementとlastChildElementはfirstElementとlastElementって命名しようかと思ったけど、childだということを意識したかったのでちょいと冗長だけど、これで堪忍してや。で、意味もそのまんま。textNodeをすっ飛ばして最初や最後のelementNodeを返す。
childElementsは、前回のポストの結果を考慮してfirstChildとnextSiblingを採用。elementNodeの配列を返す。childElementは、index指定でelementNodeを返す。本当は、こんな感じでchildElementsメソッドの返す配列のindexを返す方がすっきりしていていいんだけどなぁ。
-
childElement: function(element, index) {
-
return $(element).childElements()[index];
-
},
しかし、それだとnextSiblingを全て見てまわってしまうので、遅くなりそうなので、不採用。
cleanWhitespaceRecursiveはついでの産物。今回作成したものとは関係がないのだけども、一応。HTMLコーダにじかにHTMLを書かれるとwhitespaceが入ってしまい、困るので再帰的に消してみることにした。JavaScriptで再帰をするときってやっぱり、ローカル変数に関数をぶちこんで、それを何度も呼び出す方がいいのかな、と思ったので、自分自身を何度も呼び出すような方は不採用。
自分自身を呼び出すのは、こんな感じか。
-
cleanWhitespaceRecursive: function(element) {
-
element = $(element);
-
var child = element.cleanWhitespace().fistChild;
-
while (child) {
-
if (child.nodeType == 1) {
-
child.cleanWhitespaceRecursive();
-
}
-
child = child.nextSibling;
-
}
-
return element;
-
}
どっちがいいんだろうなぁ。。。
で、本当は、オプションで、$指定で返すか、そのままのelementを返すかを指定できるようにするかで迷ったのだけど、prototype.jsと一緒に使うことを前提としているので、$で返すようにした。これもパフォーマンスに関係してくるんだけど、まぁ、その辺は考慮中。
つーか、また後で追記したり、ソース修正するかもしれん。名前はなんでもよかったのだけど、domx.jsとしよう。
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<html lang="ja">
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
<meta http-equiv="Content-Style-Type" content="text/css" />
-
<meta http-equiv="Content-Script-Type" content="text/javascript" />
-
<script language="JavaScript" type="text/javascript" src="js/prototype.js"></script>
-
<script language="JavaScript" type="text/javascript" src="js/scriptaculous.js"></script>
-
<script language="JavaScript" type="text/javascript" src="domx.js"></script>
-
<script language="JavaScript" type="text/javascript">
-
Event.observe(window, 'load', function() {
-
var mainContainer = $('main');
-
var fc = mainContainer.firstChildElement();
-
fc.style.backgroundColor = '#eee';
-
-
var lc = mainContainer.lastChildElement();
-
lc.style.backgroundColor = '#aaa';
-
-
var ce = mainContainer.childElement(1);
-
ce.style.backgroundColor = '#ddd';
-
-
var ne = ce.nextElement();
-
ne.style.backgroundColor = '#ccc';
-
-
var pe = lc.previousElement();
-
pe.style.backgroundColor = '#bbb';
-
-
$A(mainContainer.childElements()).each(function(e) {
-
e.style.margin = '0.5em';
-
});
-
-
var sushinoneta = mainContainer.getElementsByClassNameAndTagName('sushinoneta', 'input');
-
$A(sushinoneta).each(function(neta) {
-
neta.style.marginLeft = '20px';
-
switch (neta.name) {
-
case 'tako':
-
neta.value = '380';
-
break;
-
case 'ikura':
-
neta.value = '420';
-
break;
-
case 'uni':
-
neta.value = '600';
-
break;
-
default:
-
neta.value = '360';
-
break;
-
}
-
});
-
var before = $('before');
-
before.appendChild(Builder.node('h1', 'Before cleanWhitespaceRecursive'));
-
before.appendChild(Builder.node('textarea', {cols: '100', rows: '5'}, mainContainer.innerHTML));
-
mainContainer.cleanWhitespaceRecursive();
-
var after = $('after');
-
after.appendChild(Builder.node('h1', 'After cleanWhitespaceRecursive'));
-
after.appendChild(Builder.node('textarea', {cols: '100', rows: '5'}, mainContainer.innerHTML));
-
});
-
</script>
-
<title>DOMX Demo</title>
-
</head>
-
<div id="main">
-
<span>tako:</span>
-
<input type="text" name="tako" class="sushinoneta" />
-
</label>
-
</div>
-
<span>ika:</span>
-
-
<input type="text" name="ika" class="sushinoneta" />
-
</label>
-
</div>
-
</label>
-
</div>
-
-
</label>
-
</div>
-
<span>maguro:</span>
-
-
<input type="text" name="maguro" class="sushinoneta" />
-
</label>
-
</div>
-
</div>
-
<div id="before"></div>
-
<div id="after"></div>
-
</body>
-
</html>
ソースはここ。
domx.js
ライセンスは適当っす。自己責任で使ってちょ。
で、上のソースを動かす形にしたデモもここに置いておく。domx demo
うーん。そろそろjQuery使ってみようかなぁ。。。onReadyとか便利そうだし。。。インデントがタブなのが非常に嫌なので、敬遠しているのだけど。。。
全然関係ないが、ここ二日ほど愛知県図書館で開発をしている。なかなか良いね。ホットスポットもあるみたいだけど、契約をしていないのでインターネットにつなげない。でも、そのおかげで効率がいいよん。インターネットがあるとダラダラしちゃって、ダミだ。

