堂Di

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一般通过文本节点的nodeValue来读取或改变文本节点中的文本。该文本节点通常是元素节点的firstChild

var x = document.getElementById('test');
alert(x.firstChild.nodeValue);
x.firstChild.nodeValue = "I never hack text nodes."

x.firstChild只在该文本节点确实是元素的第一个子节点时可以工作。如果不是那样的话,访问该文本节点会有些困难。

<p id="test"><br/>I am a JS hacker.</p>

现在 x.firstChild是元素节点<br/>而它没有nodeValue值。你只有通过 x.lastChild 或 x.childNodes[1]来访问该文本节点。

 

空文本节点

普通的文本节点很容易处理。不幸的是,还有空文本节点这样的东西。它们是最没用却烦人的W3C DOM特性。

<body>
<h1>Hello word!</h1>
<p> I am a JS hacker!</p>
</body>

你认为<body>有多少个子节点?2个?那么你错了。<body>有5个子节点。其中有3个是空文本节点。它们是标签之间的文本,一个硬回车或空格。

 

举例来说,看这段脚本:

var x = document.getElementsByTagName('p')[0];

x.parentNode.insertBefore(x,x.previousSibling)

这看起来很简单,取得段落并把它插入到前一个兄弟节点(h1)之前。

不幸的是,在(除Explorer外的)所有浏览器中,<p>的前一个兄弟节点不是<h1>而是</h1>与<p>之间的空文本节点。DOM树改变了,但不是按我们希望的那样

 


一个移除这些兼容性问题的方法是去掉所有空文本节点。但这让HTML变得更让人发狂了。

所以,空文本节点无法避免,最大的受害者是previousSibling 和 nextSibling. 

还有childNodes[]这个节点列表充满空文本节点因而很少被用到。

 

 

让我们试着让previousSibling能工作起啦。我们想把<p>插入到<h1>之前

var x = document.getElementByTagName('p')[0];

var previousElement = x.previousSibling;

while(previousElement.nodeType == 3 )

        previousElement = previousElement.previousSbling

x.parentNode.insertBefore(x,previousElement);

我们取得元素的previousSibling。如果是一个文本节点,我们就在获取previousSibling的前一个兄弟节点,直到遇到一个非文本节点。

此处有一个隐藏的假设:p的父节点决不会包含 真正文本的文本节点。如果你确信这样,这个例子就能工作。

 

最保险的解决方案就是根本不用使用previousSibling

var x = document.getElementsByTagName('p')[0];
var y = document.getElementsByTagName('h1')[0];
x.parentNode.insertBefore(x,y);

 

 

==================================================================================

 

节点列表

getElementsByTagName()是最重要的一个,让我们仔细看看节点列表和它们的危险性。

表面上,节点列表看起来像数组。它们接受一对方括号中的索引数字,并返回该索引数的节点。

var x = document.getElementsByTagName(‘p’)[1];

x 是文档中的第二个段落(索引为1)

 

所有的节点列表有一个length属性,它给出了节点列表的长度:

var x = document.getElementsByTagName('p').length;

x就是文档中段落的数量。

这2个特性结合起来让我们可以把节点列表当作数组一样使用:

var x = document.getElementsByTagName('p');

for(var i=0;i<x.length;i++){

    //操作每一个段落

}

 

危险

但是节点列表不是静态的。事实上,它们是危险的动态结构,因为它们会立即反映文档中的任何变化。

例如,当我们遍历文档中所有<tr>或<div>标签,把它们其中拥有rel属性的元素移动到waitingRoom 中。表面上看起来这很简单。问题是当移动tr[1]的时候列表立即更新了,tr[2]变成了tr[1],脚本前进到索引[2],它永远无法检查这个<tr>

辅助数组是一个又好又简单的解决方案。当我需要遍历一个节点列表,并从它的元素中删除一些时,我总是用这个方法。原理很简单,如果某个元素应该被删除,我就把它加入辅助数组。对节点列表的for()循环结束后,我就开始一个新的循环,遍历该数据,并从文档中把这些tr删除。

使用辅助数组的时候,我总是使用push()的shift(),这是数组的两个方法。另外,请注意辅助数组在第二次循环后就没有存在的必要了:它已经完成了它的任务。

var containers = document.getElementsByTagName(containerTag);

var hiddenFields = new Array;

for(var i=0;i<containers.length;i++){

     if(containers[i].getAttribute('rel')){

               //做点什么

              hiddenFields.push(containers[i]);

      //arrayObject.push(newelement1,newelement2,....,newelementX)

    //arrayObject.pop()将删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。

 

 

     }

}

while(hiddenFields.length){

        //其他操作,包括创建newMarker

        hiddenFields[0].parentNode.replaceChild(newMarker,hiddenFields[0]);//objDocumentNode = xmlDocumentNode.replaceChild(newChild,oldChild);

        waitingRoom.appendChild(hiddenFields.shift()); 

}

这段脚本会遍历所有的<tr>并且,如果某一个拥有rel属性,就把它压进辅助数组hiddenFields。在遍历完成后,hiddenFields就会包含所有指向需要被删除的tr的引用。

然后,脚本启动一个while()循环,在一些操作后,脚本就会把tr替换成标识符,并且把它加入waitingRoom这会讲它从文档中删除,但它不会从hiddenFileds中消失。hiddenFileds毕竟是真实的数组,而不是节点列表。

正因如此,所以脚本最后一行使用了shift() 这哥方法会返回数组中的第一个元素,并同时把它从数组中删除。因为从数组中删除了tr,所以下一个元素变成了hieenFields[0],接着循环,直到hiddenFields当中再也没有元素(即它的length为0)

 

 

posted on 2012-08-14 10:49  堂Di  阅读(429)  评论(0编辑  收藏  举报