DOM(一)

时间:2022-03-12 03:44:54
  • DOM可以将任何HMLT或XML文档描绘成一个由多层节点构成的结构,节点氛围几种不同的类型,每种类型分别表示文档中不同的信息及标记,每个节点都拥有各自的特点、数据和方法。
  • Node类型

DOM1级定义了一个Node接口,该接口将由DOM中的所以节点类型实现,除了IE之外,其他浏览器都可以访问到这个类型,js中的所有节点类型都继承自Node类型。

每个节点都有一个nodeType属性,用于表明节点的类型,下面12个数值常来表示类型:

  1. Node.ELEMENT_NODE(1);
  2. Node.ATTRIBUTE_NODE(2);
  3. Node.TEXT_NODE(2);
  4. Node.CDATA_SECTION_NODE(4);
  5. Node.ENTITY_REFERENCE_NODE(5);
  6. Node.ENTITY_NODE(6);
  7. Node.PROCESSING_INSTRUCTION_NODE(7)
  8. Node.COMMENT_NODE(8)
  9. Node.DOCUMENT_NODE(9)
  10. Node.DOCUMENT_TYPE_NODE(10)
  11. Node.DOCUMENTY_FRAGMENT_NODE(11)
  12. Node.NOTATION_NODE(12)

通过比较上面这些常量,可以很容易地确定节点的类型,例如:

If(someNode.nodeType==Node.ELEMENT_NODE){//在ie中无效

     alert(“Node is an element”);

}

由于ie没有公开Node类型的构造函数,因此上面的代码在ie中会导致错误,为了确保跨浏览器兼容,最好还是将nodeType属性和数字进行比较,如下:

 if(someNode.nodeType==1){

       alert(“Node is an element.”);

}

要了解节点的具体信息,可以使用nodeName和nodeValue这两个属性,这两个属性的值完全取决于节点的类型,在使用这两个值以前,最后是像下面这样先检测一下节点的类型。

if(someNode.nodeType==1){

  value=someNode.nodeName;

}
  • 节点关系

每个节点都有一个childNodes属性,其中保存着一个NodeList对象,NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。虽然可以通过方括号语法来方位NodeList的值,而且这个对象也有length属性,但它并不是Array的实例。NodeList对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反应在NodeList对象中。

下面例子展示了如何访问保存在NodeList中的节点,可以通过方括号,也可以使用item方法。

var firstChild=someNode.childNodes[0];

var secondChild=someNode.childNodes.item(1);

var count=someNode.chilNodes.length;

对arguments对象使用Array.prototype.slice()方法可以将其转换为数组,而采用同样方法,也可以将NodeList对象转换为数组,如下:

var arrayOfNodes=Array.prororype.slice.call(someNode.childNodes,0)’

除IE8及更早版本之外,这行代码能在任何浏览器中运行,由于IE8及更早版本将NodeList实现为一个COM对象,而我们不能像使用Jscript对象那样使用这种对象,因此上面的代码会导致错误,要想在ie中将NodeList转换为数组,必须手动枚举所有成员。如下:

function convertToArray(nodes){

  var array=null;

  try{

    array=Array.prororype.slice.call(nodes,0);

}catch(ex){

  Array=new Array();

  for(var i=0,len=nodes.length;i<len;i++){

    array.push(nodes[i]);

}

}

return array;

}

每个节点都有一个parentNode属性,该属性指向文档树中的父节点,包含在childNodes列表中的所有节点都具有相同的父节点,因此它们的parentNode属性都指向同一个节点。此外,包含在childNodes列表中的每个节点相互之间都是同胞节点,通过使用列表中每个节点的previousSibling和nextSibling属性,可以访问同一列表中的其他节点,列表中的第一个节点的previousSibling属性为null,而列表中最后一个节点的nextSibling属性的值同样为null,如下:

if(someNode.nextSibling===null){

  alert(“Last node in the parent’s childNodes list.”);

}else if(someNode.previousSibling===null){

  alert(“First node in the parent’s  childNodes list.”);

}

父节点的firstChild和lastChild属性分别指向其childNodes列表中的第一个和最后一个节点,其中,someNode.firstChild的值始终等于someNode.childNodes[0],而someNode.lastChild的值始终等于someNode.childNodes[someNode.childNodes.length-1]。在只有一个节点的情况下,firstChild和lastChild指向同一个节点,如果没有子节点,那么firstChild和lastChild的值均为null。

所有节点都有的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点,这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于亮哥或更多个文档中,通过这个属性,我肯可以不必再节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。

DOM提供了一些操作节点的方法,其中,最常用的方法是appendChild,用于向childNodes列表的末尾添加一个节点,添加节点后,childNodes的新增节点、父节点及以前的最后一个子节点的关系指针都会相应得到更新,更新完成后,appendChild返回新增的节点,如下:

var returnedNode=someNode.appendChild(newNode);

alert(returnedNode==newNode);               //true

alert(someNode.lastChild==newNode);          //true

如果传入到appendChild中的节点已经是文档的一部分,那结果就是将该节点从原来的位置转移到新位置,如下:

var returnedNode=someNode.appendChild(someNode.firstChild);

alert(returnedNode==someNode.firstChild);   //false

alert(returnedNode==someNode.lastChild);   //true

如果需要把节点放在childNodes列表中某个特定的位置上,而不是放在末尾,那么可以使用insertBefore方法,这个方法接受两个参数:要插入的节点和作为参照的节点,插入节点后,被插入的节点会变成参照节点的前一个同胞节点,同时被方法返回。如下例子:

returnedNode=someNode.insertBefore(newNode,null);

alert(newNode==someNode.lastChild);          //true

var returnedNode=someNode.inserBefore(newNode,someNode.firstChild);

alert(retrunedNode==newNode);              //true

alert(newNode==someNode.firstChild);         //true

returnedNode=someNode.insertBefore(newNode,someNode.lastChild);

alert(newNode==someNode.chlidNodes[someNode.childNodes.length-2]);            //true

replaceChild方法接受的两个参数是:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置,如下:

var returnedNode=someNode.replaceChild(newNode,someNode.firstChild);

returnedNode=someNode.replaceChild(newNode,someNode.lastChild);

使用replaceChild插入一个节点时,该节点的所有关系指针都会从被它替换的节点复制过来。

removeChild方法移除节点。这个方法移除的节点仍然为文档所有,只不过在文档中已经没有了自己的位置。

cloneNode方法用于创建调用这个方法的节点的一个完全相同的副本,cloneNode方法接受一个布尔值参数,表示是否执行深复制,在参数为true的情况下,执行深复制,也就是在复制节点及其整个子节点树,在参数为false的情况下,执行浅复制,即只复制节点本身,复制后返回的节点副本属于文档所有。

  • Document类型

Js通过Document类型表示文档,在浏览器中,document对象是HTMLDocument的一个实例,表示整个html页面。而且,document对象是window对象的一个属性,Document节点具有下列特性:

  1. nodeTye的值为9;
  2. nodeName的值为“#document”;
  3. nodeValue的值为null;
  4. parentNode的值为null;
  5. ownerDocument的值为null;
  6. 其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Coment。

Document类型可以表示HTML页面或者其他基于XML的文档,不过,最常见的应用还是作为HTMLDocument实例的document对象,通过整个文档对象,不仅可以取得与页面的有关信息,而且还能操作页面的外观及其底层结构。

  • 文档的子节点

虽然DOM标准规定Document节点子节点可以是DocumentType、Element、ProcessingInstructior或Comment,但还有两个内置的访问其子节点的快捷键,第一个就是documentElement属性,该属性始终指向HTML页面中的<html>元素。另一个就是通过childNodes列表返回文档元素,但通过documentElement属性则更快捷,更直接的访问该元素。

作为HTMLDocument的实例,document对象还有一个body属性,直接指向body元素,因为开发人员经常要使用这个元素,所以document.body在js代码中出现的频率非常高,如下:

var body=document.body;

所有浏览器都支持document.documentElement和document.body属性。

Document另一个可能的子节点时DocumentType,通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性来访问它的信息。

var doctype=document.doctype;

浏览器对document.doctype的支持差别很大,总结如下:

  1. IE8之前版本:如果存在文档类型声明,会将其错误地理解为一个注释并把它当作Comment节点;而document.doctype的值始终未null。
  2. IE9+及Firefox:如果存在文档类型声明,则将其作为文档的第一个子节点;document.doctype是一个DocumentType节点,也可以通过document.firstChild或document.childNodes[0]访问同一个节点。
  3. Safari、Chrome和Opera:如果存在文档类型声明,则将其解析,但不作为文档子节点,document.doctype是一个DocumentType节点,但该节点不会出现在document.childNodes中。

如下:

<!--第一条注释 -->

<html>

   <body>

   </body>

</html>

<!—-第二条注释-->

看起来这个页面应该有3个节点:注释、html元素、注释。现实中浏览器在处理位于html外部的注释方面存在如下差异:

  1. IE8之前版本、Safari 3.1及更高版本、Opera和Chrome职位第一条注释创建节点,不为第二天注释创建节点,结果,第一条注释就会成为document.childNodes中的第一个子节点
  2. IE9及更高版本会将第一条注释创建为document.childNodes中的一个注释节点,也会将第二条注释创建为document.childNodes中的注释子节点。
  3. Firefox以及Safari 3.1之前的版本会完全忽略这个两条注释。