最常见的JavaScript DOM方法实战-文档遍历 | CSSrainbow.cn

Node接口定义的节点类型都包含的特性和方法

特性和方法后面的 “冒号:” 紧跟的单词是“返回值类型

Node
属性

遍历节点(短途旅行):
parentNode : Node
firstChild : Node
lastChild : Node
nextSibling : Node
previousSibling : Node
childNodes : NodeList
节点信息:
nodeName :String
nodeType :number
nodeValue :String
返回一个节点的根元素(文档对象):
ownerDocument : Document
包含了代表一个元素的特性的Attr对象;仅用于Element节点:
attributes : NamedNodeMap
获取对象层次中的父对象:
parentElement [IE] :Node
方法

修改文档树:
appendChild(Node newChild) : Node
insertBefore(Node newChild, Node refChild) : Node
removeChild(Node oldChild): Node
replaceChild(Node newChild, Node refChild) : Node
克隆一个节点:
cloneNode(boolean deep) : Node
删除一个子节点:
removeNode(boolean removeChildren) : Node
判断childNodes包含是否包含节点:
hasChildNodes() : boolean

Node

Document
属性

自己的:
documentElement : Element
继承 Node :
attributes, childNodes, firstChild, lastChild, nextSibling, nodeName, nodeType, nodeValue, ownerDocument, parentElement, parentNode, previousSibling
方法

自己的:
创建元素:
createElement(String tagName) : Element
createTextNode(String data) : Text
查找元素:
getElementById(String elementId) : Element
getElementsByTagName(String tagname) : NodeList
继承 Node :
appendChild, cloneNode, hasChildNodes, insertBefore, removeChild, removeNode, replaceChild

Element
属性

自己的:
tagName: String
继承 Node :
attributes, childNodes, firstChild, lastChild, nextSibling, nodeName, nodeType, nodeValue, ownerDocument, parentElement, parentNode, previousSibling
方法

自己的:
属性的读写:
getAttribute(String name) : String
setAttribute(String name, String value) : void
其它:
getElementsByTagName(String name) Stub : NodeList
normalize() Stub : void
removeAttribute(String name) : void
继承 Node :
appendChild, cloneNode, hasChildNodes, insertBefore, removeChild, removeNode, replaceChild

访问相关的节点

我们还是从 HTML页面 最基本的组成元素讨论起,

1  2  3  4  5  6  7  8  
<!--<html>      <head>          <title>DOM Examlie</title>      </head>      <body>         <p>Hello World !</p>  </body>  </html>-->

我要访问 <html>元素,你应该明白它是该文件的document元素,那你就可以使用document的documentElement属性

1  2  3  
var oHtml=document.documentElement;//可以直接访问<html>元素  alert("节点名称 : "+oHtml.nodeName);//节点名称  alert("节点类型 : "+oHtml.nodeType);//节点类型为 1

获取<head> 和 <body>元素

1  2  
var oHead=oHtml.firstChild;//HEAD节点  var oBody=oHtml.lastChild;//BODY节点

也可以通过childNodes属性,获取<head> 和 <body>元素

1  2  3  4  
var oHead=oHtml.childNodes.item(0);//HEAD节点  //var oHead=oHtml.childNodes[0];//简写,也有同样的结果是HEAD节点  var oBody=oHtml.childNodes.item(1);//BODY节点  //var oBody=oHtml.childNodes.item(1);//简写,也有同样的结果是BODY节点

注意:方括号标记其实是NodeList在javascript中的简便实现。实际上正式的从childNodes列表中获取子节点的方法是使用item()方法:

HTML DOM 中的专有属性 document.body ,它常用于直接访问元素

1  
    var oBody=document.body;

既然我们都知道了以上节点对象的获取方式,那我们用oHtml,oHead,oBody 这三个变量来确定一下它们之间的关系

1  2  3  4  5  
alert(oHead.parentNode==oHtml);//HEAD节点的父节点是BODY节点,返回 true   alert(oBody.parentNode==oHtml);//BODY节点的父节点是BODY节点,返回 true   alert(oBody.previousSibling==oHead);//BODY节点的上一个兄弟节点是HEAD节点 ,返回 true  alert(oHead.nextSibling==oBody);//HEAD节点的下一个兄弟节点是BODY节点,返回 true  alert(oHead.ownerDocument==document); //返回一个节点的根元素(Document),HEAD节点是否指向该文档,返回 true

通过上面的学习我们已经了解遍历节点的最基本的方式, 也学会了如何找到某一个节点的兄弟节点及它的子节点。它们之间的关系你是否理解透彻了那?不是很明白也没关系,下面就是我给出的节点关系图


节点关系图

节点之间的关系图 

复杂的节点遍历

在上面的学习中我们好像没有遇到过大的阻碍,下面我在一我的导航条为例还讨论节

点遍历。

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  
<div id="menu">      <h1>我的导航条</h1>      <ul id="nav">         <li><a href="#">HOME</a></li>         <li><a href="#">(X)Html / Css</a></li>         <li><a href="#">Ajax / RIA</a></li>         <li><a href="#">GoF</a></li>         <li><a href="#">JavaScript</a></li>         <li><a href="#">JavaWeb</a></li>         <li><a href="#">jQuery</a></li>         <li><a href="#">MooTools</a></li>         <li><a href="#">Python</a></li>         <li><a href="#">Resources</a></li>      </ul>  </div>

DOM-Properties

我又给出了一幅节点之间的关系图,是针对我的导航条。

首先我想把看一下我的导航条下有多少个子节点。
我第一想到的是前面我学过的查找元素的2种方法:

getElementById() # 通过ID属性查找元素
该方法将返回一个与那个有着给定id属性值的元素节点相对应的对象。
getElementsByTagName() # 通过标签名称查找元素
该方法返回一个对象数组,每个对象分别对应着文档里有着给定标签的一个元素。
1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  
<script type="text/javascript">  /*  通过ID属性查找元素 ,用的是文档对象的getElementById()方法,  查找到我们想要的元素对象,根据这个节点元素的 childNodes 属性,  遍历出所有的子节点对象。  */  function queryElementsId(){    var elemensArray,nav,nav_list;    elemensArray=[];    nav=document.getElementById("nav");    /*注意IE和FF中处理Text节点上存在着一些差异*/       nav_list=nav.childNodes;       for(var i=0;i<nav_list.length;i++){      elemensArray[elemensArray.length]=nav_list[i];      //elemensArray.push(nav_list[i]); //同上一样的结果       }    return elemensArray;     }  /*  我们观察到我的导航条是有规律的,是用无序列表元素组成的,只有定位到 &lt;ul&gt;元素  ;然后把getElementsByTagName()方法可以返回相同元素对象的集合,  查用它找一组元素,太方便了。  */  function queryElementsTagName(){   var elemensArray,nav,nav_list;   elemensArray=[];   var nav=document.getElementById("nav");   var nav_list=nav.getElementsByTagName("li");//返回相同的一组元素   for(var i=0;i<nav_list.length;i++){    elemensArray[elemensArray.length]=nav_list[i];    //elemensArray.push(nav_list[i]); //同上一样的结果   }   return elemensArray;     }  </script>

节点遍历

那我们接下来,测一下是否是我们想要的东西:

1  2  3  4  5  6  7  8  9  10  11  12  13  
<script type="text/javascript">  window.onload=function(){       /*第一个方法*/       var list= queryElementsId();    /*第二个方法*/   //var list= queryElementsTagName();    var s="";   for(var i=0;i<list.length;i++){    s+=list[i].nodeName+"\n";      }   alert(s);   }  </script>

注意:为什么把处理的方法放到window.onload中的原因我就不多说了。

我有一个专题中专门介绍了各种window.onload的解决方案。

请看 关于window.onload加载的多种解决方案
日志篇。

先看一下第一个方法 queryElementsId() 好像我们在IE中没有发现有什么问题,那我们在Firefox中看一下是否也是我们想要的结果。

IE中的DOM遍历:

IE DOM

FF中的DOM遍历:

FF DOM

通过上面的测试,我们发现了一个严重的问题。

那就是,不同的浏览器在判断何为Text节点上存在着一些差异,例如在A级浏览器中的FF和IE就有很大的差异,

FF会把元素之间的空白、换行、tab都是Text节点,

IE下会把空白全部忽略掉,只有内联元素(如em,span)后的换行、空格、tab会被认为是一个Text。

既然遇到了问题那我们就得解决问题,问题的根源我们也知道了,那相应的解决方案就好做了。

我这里整理出3种方法,供大家参考。

方法一:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  
<script type="text/javascript">  /*  《精通javascript》上提供了一个函数,用于处理xm中的这些空格,其作用原理就是找出文本节点,并删除这些节点,以达到删除这些空格的目的。  */     function cleanWhitespace(element){      //如果不提供参数,则处理整个HTML文档      element = element || document;      //使用第一个子节点作为开始指针      var cur = element.firstChild;         //一直到没有子节点为止      while (cur != null){          //如果节点为文本节点,应且包含空格          if ( cur.nodeType == && ! /\S/.test(cur.nodeValue)){              //删除这个文本节点              element.removeChild( cur );                 //否则,它就是一个元素          } else if (cur.nodeType == 1){              //递归整个文档              cleanWhitespace( cur );          }             cur = cur.nextSibling;//遍历子节点      }  }  </script>

方法二:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  
<script type="text/javascript">     /*  最后,利用数组写了一个函数,能够有效的处理dom中的空格,其原理就是将一个元素的的父元素找出来,然后通过它父元素的childNodes属性找出该元素的所有兄弟元素。遍历该元素和它的兄弟元素,将所有元素节点放在一个数组里。这样调用这个数组,就只有元素节点而没有文本节点,也就没有了讨厌的空格.     */     function cleanWhitespaces(elem){      //如果不提供参数,则处理整个HTML文档   var elem = elem || document;   var parentElem = elem.parentNode; //返回一个节点的父类节点   var childElem = parentElem.childNodes; //返回一个节点的子节点的节点列表   var childElemArray = new Array;   for (var i=0; i<childElem.length; i++){    if (childElem[i].nodeType==1){//把所有节点是元素节点类型的节点存放到数组里     childElemArray.push(childElem[i]);    }   }      return childElemArray;  }  </script>

方法三:推荐

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  
<script type="text/javascript">  /*  原理是对元素的所有的子节点做一个遍历。然后做一个判断,如果是子元素节点(nodeType = 1),则遍历该子元素的所有的子节点,用递归检查是否包含空白节点;如果处理的子节点是文本节点(nodeType = 3),则检查是否是纯粹的空白节点,如果是,就将它从xml对象中删除。  */  function removeWhitespace(xml){      var loopIndex;         for (loopIndex = 0; loopIndex < xml.childNodes.length; loopIndex++){          var currentNode = xml.childNodes[loopIndex];          if (currentNode.nodeType == 1){              removeWhitespace(currentNode);          }             if (((/^\s+$/.test(currentNode.nodeValue))) &&(currentNode.nodeType == 3)){              xml.removeChild(xml.childNodes[loopIndex--]);          }      }  }  </script>

好了,我们在验证一下,#Text节点问题是否处理掉了。那我们就用方法3 中removeWhitespace(nav)方法来处理queryElementsId()方法中的#Text节点问题。

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  
<script type="text/javascript">  function queryElementsId(){    var elemensArray,nav,nav_list;    elemensArray=[];    nav=document.getElementById("nav");    /*处理#Text节点问题*/    removeWhitespace(nav);       /*注意IE和FF中处理Text节点上存在着一些差异*/       nav_list=nav.childNodes;       for(var i=0;i<nav_list.length;i++){      elemensArray[elemensArray.length]=nav_list[i];      //elemensArray.push(nav_list[i]); //同上一样的结果       }    return elemensArray;     }  </script>

正如我想看到的结果,IE和FF中都OK 了

FF IE DOM

一个比较通用的方法

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  
<script type="text/javascript">  function text(elem){   var t="";   //如果传入的是元素,则继续遍历其子元素   //否则假定它是一个数组   elem=elem.childNodes || elem;   //遍历所有子节点   for(var i=0; i<elem.length;i++){       //如果不是元素,追加其文本值    //否则,递归遍历所有元素的子节点    t+=elem[i].nodeType !=1?elem[i].nodeValue:text(elem[i].childNodes);      }      //返回比配的文本      return t;  }  </script>

《我的导航条》中相关节点的访问(父节点,子节点,兄弟节点)

让我再熟悉一下上面学过的知识点,实践是检验真理的唯一标准。
那就开始吧!

DOM-Properties

用元素节点的DOM属性遍历DOM树

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  
<script type="text/javascript">  window.onload=function(){    /*定位想要的节点*/    var nav=document.getElementById("nav");    /*查找父节点*/    var p_n=nav.parentNode;    alert("父节点的元素名称:"+p_n.nodeName);       /*处理FF遍历节点中的#Text */    removeWhitespace(nav);//移除所有的空Text节点       /*查找子节点*/    var c_n_f=nav.firstChild;//第一个节点对象    //var c_n_f=nav.childNodes[0];//同上一样的结果    var c_n_l=nav.lastChild;//最后一个节点对象    //var c_n_l=nav.childNodes[nav.childNodes.length-1];//同上一样的结果    alert("第一个节点:"+c_n_f.nodeName+"  "+"最后一个节点 :"+c_n_l.nodeName);    /*查找兄弟节点 或叫 相邻节点 */    /*用nextSibling和PreviousSibling必须有一个参考点,这样指针才知道自己往那里移动*/    var c_n_s=c_n_f.nextSibling;//第一个节点的下一个节点    alert("第一个节点的下一个节点:"+c_n_s.innerHTML+ "\n" + "节点中包含的HTML内容: "+c_n_s.nodeName);     }     </script>

遍历节点的相关属性和辅助的方法,我们大部分都应用到了,有更多的方法和技巧还等待我们去学习和发现。

写到这里,既然标准的previousSibling,nextSibling,firstChild,lastChild,parentNode遍历方法有浏览器不兼容问题。我上面的解决方案是去掉遍历元素的相关空的#Text节点,是一个好的解决方案,但是使用起来不方便,我们何不自己写一些遍历节点的方法来代替标准的的previousSibling,nextSibling,firstChild,lastChild,parentNode。

我们的思路是利用元素是nodeType属性来判断元素是节点类型中那种节点类型,在DOM节点中我最常用的是元素节点,文本节点,属性节点,对应的类型值是
元素节点 nodeType=1 or ELEMENT_NODE, 文本节点 nodeType=2 or ATTRIBUTE_NODE,属性节点 nodeType=3 or TEXT_NODE,但是IE中并不支持命名常量,那就用数值吧,再配合标准的遍历属性。完全可以自己生产一些辅助函数来取代标准的遍历方式。

以下一系列的辅助函数可以帮助您,他们能取代标准的previousSibling,nextSibling,firstChild,lastChild,parentNode;

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  
<script type="text/javascript">  //---------DOM 遍历,如果元素没找到则返回null---------//          //---查找相关元素的前一个兄弟元素---//         function prev(elem){          do{             elem=elem.previousSibling;          }while(elem && elem.nodeType!=1);          return elem;      }      //---查找相关元素的下一个兄弟元素---//          function next(elem){         do{             elem=elem.nextSibling;          }while(elem && elem.nodeType!=1);          return elem;      }      //---查找第一个子元素的函数---//          function first(elem){          elem=elem.firstChild;          return elem && elem.nodeType!=1 ?next(elem):elem;      }      //---查找最后一个子元素的函数---//          function last(elem){          elem=elem.lastChild;          return elem && elem.nodeType!=1 ?prev(elem):elem;      }      //---查找父级元素的函数---//          //num是父级元素的级次,parent(elem,2)等价于     function parent(elem,num){          num=num||1;          for(var i=0; i<num; i++){              if(elem!=null){                  elem=elem.parentNode;             }          }         return elem;     }  </script>

在下一节中我们来讨论如何修改文档树.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值