一般通过文本节点的nodeValue来读取或改变文本节点中的文本。该文本节点通常是元素节点的firstChild var x = document.getElementById('test'); x.firstChild只在该文本节点确实是元素的第一个子节点时可以工作。如果不是那样的话,访问该文本节点会有些困难。 <p id="test"><br/>I am a JS hacker.</p> 现在 x.firstChild是元素节点<br/>而它没有nodeValue值。你只有通过 x.lastChild 或 x.childNodes[1]来访问该文本节点。
空文本节点 普通的文本节点很容易处理。不幸的是,还有空文本节点这样的东西。它们是最没用却烦人的W3C DOM特性。 <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树改变了,但不是按我们希望的那样
所以,空文本节点无法避免,最大的受害者是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];
==================================================================================
节点列表 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) |