在SVG文档中遍历子节点的问题
修订:
2007-03-12 删除了BATIK不计算空白节点的描述,因为在实际编程中发现BATIK1.6也将空白节点计算为子节点。不知道 www.carto.net中对BATIK节点计算的描述是否基于早期的BATIK版本。
相信很多使用Adobe SVG Viewer(ASV)进行SVG编程的都遇到过这个问题,在使用节点遍历时,返回的节点数总是有些奇怪,例如在SVG文档中如果写入:
<g id="choices">
<!--this is a test-->
<text x="20" y="100" font-size="30">by their</text>
<text x="20" y="100" font-size="30">by their</text>
</g>
然后使用如下代码取该组下的子节点数量:
var elem = svgDocument.getElementById("choices");
var nodes = elem.childNodes;
alert(nodes.length);
我们期望返回的个数为3,包括一个注释和两个文本节点,但ASV实际返回为7。网上有些文档解释如下:
childNodes
中子节点列表的序号是从
1
开始,并且以
2
递增的。因此访问的方式为
for(var i = 1;i < ((nodes.length-1));i = i+2)
{
alert(printNode(nodes.item(i)));
}
事实上这是不对的,在ASV中,节点下的空白也作为一个节点计算,因此上面的节点还要加上四个空白节点,因此是7个。如果我们在SVG中以不换行的方式写上述节点,
<g id="choices"> <!--this is a test--> <text x="20" y="100" font-size="30">by their</text> <text x="20" y="100" font-size="30">by their</text></g>
这时返回的节点数是3。在这种情况下,上面的代码就有问题了。特别是在我们动态生成SVG图形时,这个问题比较比较明显了。例如在原始的SVG文档中有:
<g id="choices">
</g>
然后在脚本中,使用createElementNS在组中动态创建几个矩形图形节点:
var group = svgDocument.getElementById("choices ");
for(var i=0; i < 3; i++)
{
var elem = svgDocument.createElementNS(svgns, "rect" );
group.appendChild(elem);
}
此时组中的子节点数量为4(三个矩形节点和一个空白节点),对矩形子节点的遍历就应当是:
var elem = svgDocument.getElementById("choices");
var nodes = elem.childNodes;
for(var i = 1; i < nodes.length ;i ++)
{
alert(nodes.item(i).nodeName);
}
但如果原始的文档中写为
<g id="choices"></g>
在动态创建子节点后,组中子节点数量为3,对矩形子节点的遍历的遍历为:
var elem = svgDocument.getElementById("choices");
var nodes = elem.childNodes;
for(var i = 0; i < nodes.length ;i ++)
{
alert(nodes.item(i).nodeName);
}
可见遍历循环的条件取决于原始文档中节点的写法,这是我们不希望的。因此,正确的方法是,
总是遍历所有子节点,然后根据节点的名称进行过滤。
var elem = svgDocument.getElementById("choices ");
var nodes = elem.getChildNodes;
for(i = 0;i < nodes.length ;i ++)
{
if(nodes.item(i).nodeName == "#text")
continue;
// Do something
}
或者
var elem = svgDocument.getElementById("choices ");
var nodes = elem.getChildNodes;
for(i = 0;i < nodes.length ;i ++)
{
if(nodes.item(i).nodeName == "rect")
{
// Do something
}
}
特别是在编写可以不同的SVG浏览器中移植的代码时,使用上述方式尤为重要,因为不同的浏览器对空白处理子是不一样的。