DOM(文檔對象模型)

一、節點層次

  • Node類型javascript

    • DOM1 級定義了一個 Node 接口,該接口將由 DOM 中的全部節點類型實現,JavaScript 中的全部節點類型都繼承自 Node 類型,所以全部節點類型都共享着相同的基本屬性和方法css

    • 每一個節點都有一個 nodeType 屬性,用於代表節點的類型。節點類型由在 Node 類型中定義的下列12 個數值常量來表示,任何節點類型必居其一:html

      • Node.ELEMENT_NODE(1);
      • Node.ATTRIBUTE_NODE(2);
      • Node.TEXT_NODE(3);
      • Node.CDATA_SECTION_NODE(4)
      • Node.ENTITY_REFERENCE_NODE(5)
      • Node.ENTITY_NODE(6);
      • Node.PROCESSING_INSTRUCTION_NODE(7)
      • Node.COMMENT_NODE(8)
      • Node.DOCUMENT_NODE(9);
      • Node.DOCUMENT_TYPE_NODE(10)
      • Node.DOCUMENT_FRAGMENT_NODE(11)
      • Node.NOTATION_NODE(12)
      if (someNode.nodeType == 1){ //適用於全部瀏覽器
          alert("Node is an element.");
      }
    • nodeName 和 nodeValue 屬性:這兩個屬性的值徹底取決於節點的類型java

      if (someNode.nodeType == 1){
          value = someNode.nodeName; //nodeName 的值是元素的標籤名
        }
    • 節點關係node

      • 每一個節點都有一個 childNodes 屬性,其中保存着一個 NodeList 對象。NodeList 是一種類數組對象,用於保存一組有序的節點,能夠經過位置來訪問這些節點ios

        var firstChild = someNode.childNodes[0];//經過方括號訪問
        var secondChild = someNode.childNodes.item(1);//使用 item()方法訪問
        var count = someNode.childNodes.length; //這個對象也有 length 屬性(訪問那一刻)
        
        //以將 NodeList 對象轉換爲數組:兼容IE8及以前瀏覽器
        function convertToArray(nodes){
          var array = null;
          try {
              array = Array.prototype.slice.call(nodes, 0); //針對非 IE 瀏覽器
           } 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;若是列表中只有一個節點,那麼該節點的 nextSibling 和 previousSibling 都爲 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 列表中的第一個和最後一個節點;在只有一個子節點的狀況下,firstChild 和
        lastChild 指向同一個節點;若是沒有子節點,那麼 firstChild 和 lastChild 的值均爲 null跨域

        someNode.firstChild === someNode.childNodes[0]
        someNode.lastChild === childNodes [someNode.childNodes.length-1]
      • hasChildNodes()也是在節點包含一或多個子節點的狀況下返回 true數組

      • 全部節點都有的最後一個屬性是 ownerDocument,該屬性指向表示整個文檔的文檔節點。這種關係表示的是任何節點都屬於它所在的文檔,任何節點都不能同時存在於兩個或更多個文檔中。經過這個屬性,咱們能夠沒必要在節點層次中經過層層回溯到達頂端,而是能夠直接訪問文檔節點瀏覽器

    • 操做節點:

      • appendChild()方法:用於向 childNodes 列表的末尾添加一個節點,返回新增的節點

        //添加節點後,childNodes 的新增節點、父節點及之前的最後一個子節點的關係指針都會相應地獲得更新
        var returnedNode = someNode.appendChild(newNode);
        alert(returnedNode == newNode); //true
        alert(someNode.lastChild == newNode); //true
        
        /*若是傳入到 appendChild()中的節點已是文檔的一部分了,那結果就是將該節點從原來的位置
        轉移到新位置*/
        //someNode 有多個子節點
        var returnedNode = someNode.appendChild(someNode.firstChild);
        alert(returnedNode == someNode.firstChild); //false
        alert(returnedNode == someNode.lastChild); //true
      • insertBefore()方法:把節點放在 childNodes 列表中某個特定的位置上。這個方法接受兩個參數:要插入的節點和做爲參照的節點。插入節點後,被插入的節點會變成參照節點的前一個同胞節點(previousSibling),同時被方法返回。若是參照節點是null,則 insertBefore()與 appendChild()執行相同的操做

        //插入後成爲最後一個子節點
        returnedNode = someNode.insertBefore(newNode, null);
        alert(newNode == someNode.lastChild); //true
        //插入後成爲第一個子節點
        var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
        alert(returnedNode == newNode); //true
        alert(newNode == someNode.firstChild); //true
        //插入到最後一個子節點前面
        returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
        alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true
      • replaceChild()方法:接受的兩個參數是是要插入的節點和要替換的節點,要替換的節點將由這個
        方法返回並從文檔樹中被移除,同時由要插入的節點佔據其位置

        //替換第一個子節點
        var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
        //替換最後一個子節點
        returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
      • removeChild()方法:移除節點,接受一個參數,即要移除的節點。被移除的節點將成爲方法的返回值

        //移除第一個子節點
        var formerFirstChild = someNode.removeChild(someNode.firstChild);
        //移除最後一個子節點
        var formerLastChild = someNode.removeChild(someNode.lastChild);
      • 注意:要使用這幾個方法必須先取得父節點(使用 parentNode 屬性)。另外,並非全部類型的節點都有子節點,若是在不支持子節點的節點上調用了這些方法,將會致使錯誤發生

    • 其餘方法

      • cloneNode()方法:用於建立調用這個方法的節點的一個徹底相同的副本。cloneNode()方法接受一個布爾值參數,表示是否執行深複製;複製後返回的節點副本屬於文檔全部,但並無爲它指定父節點,因此須要經過 appendChild()、insertBefore()或 replaceChild()將它添加到文檔中

        <ul>
         <li>item 1</li>
         <li>item 2</li>
         <li>item 3</li>
        </ul> 
        <script>
            //已經將<ul>元素的引用保存在了變量 myList 中
          var deepList = myList.cloneNode(true);
          alert(deepList.childNodes.length); //3(IE < 9)或 7(其餘瀏覽器)
          var shallowList = myList.cloneNode(false);
          alert(shallowList.childNodes.length); //0 
        </script>
      • normalize()方法:處理文檔樹中的文本節點,因爲解析器的實現或 DOM 操做等緣由,可能會出現文本節點不包含文本,或者接連出現兩個文本節點的狀況。當在某個節點上調用這個方法時,就會在該節點的後代節點中查找上述兩種狀況。若是找到了空文本節點,則刪除它;若是找到相鄰的文本節點,則將它們合併爲一個文本節點

  • Document類型

    • 概念:JavaScript 經過 Document 類型表示文檔。在瀏覽器中,document 對象是 HTMLDocument(繼承自 Document 類型)的一個實例,表示整個 HTML 頁面。並且,document 對象是 window 對象的一個屬性,所以能夠將其做爲全局對象來訪問。

    • 特徵:

      • nodeType 的值爲 9
      • nodeName 的值爲"#document"
      • nodeValue 的值爲 null
      • parentNode 的值爲 null
      • ownerDocument 的值爲 null
      • 其子節點多是一個 DocumentType(最多一個)、Element(最多一個)、ProcessingInstruction
        或 Comment
    • 文檔的子節點:

      • 訪問Document 節點的子節點的快捷方式:第一個就是documentElement屬性,該屬性始終指向 HTML 頁面中的<html>元素;另外一個就是經過 childNodes 列表訪問文檔元素

        <html>
          <body>
        
          </body>
        </html> 
        <script>
          var html = document.documentElement; //取得對<html>的引用
          alert(html === document.childNodes[0]); //true
          alert(html === document.firstChild); //true
        </script>
      • document 對象還有一個 body 屬性,直接指向<body>元素

        var body = document.body; //取得對<body>的引用
      • Document 另外一個可能的子節點是 DocumentType,一般將<!DOCTYPE>標籤當作一個與文檔其餘
        部分不一樣的實體,部分不一樣的實體,能夠經過 doctype 屬性來訪問它的信息

        var doctype = document.doctype; //取得對<!DOCTYPE>的引用
      • 出如今元素外部的註釋應該算是文檔的子節點,可是不一樣的瀏覽器在是否解析這些註釋以及可否正確處理它們等方面,也存在很大差別

    • 文檔信息

      • title屬性:包含着<title>元素中的文本——顯示在瀏覽器窗口的標題欄或標籤頁上;經過這個屬性能夠取得當前頁面的標題,也能夠修改當前頁面的標題並反映在瀏覽器的標題欄中。修改 title 屬性的值不會改變

        //取得文檔標題
        var originalTitle = document.title;
        //設置文檔標題
        document.title = "New page title";
      • URL 屬性:包含頁面完整的 URL(即地址欄中顯示的 URL),不能夠設置

        //取得完整的 URL
        var url = document.URL;
      • domain 屬性:只包含頁面的域名。能夠設置,但因爲安全方面的限制,也並不是能夠給 domain 設
        置任何值

        //取得域名
        var domain = document.domain; 
        
        //假設頁面來自 p2p.wrox.com 域
        document.domain = "wrox.com"; // 成功
        document.domain = "nczonline.net"; // 出錯!
        
        //當頁面中包含來自其餘子域的框架或內嵌框架時,可以設置 document.domain 就很是方便了。因爲跨域安全限制,來自不一樣子域的頁面沒法經過 JavaScript 通訊。而經過將每一個頁面的document.domain 設置爲相同的值,這些頁面就能夠互相訪問對方包含的 JavaScript 對象了
        
        //若是域名一開始是「鬆散的」(loose),那麼不能將它再設置爲「緊繃的」(tight)
        //假設頁面來自於 p2p.wrox.com 域
        document.domain = "wrox.com"; //鬆散的(成功)
        document.domain = "p2p.wrox.com"; //緊繃的(出錯!)
      • referrer屬性:保存着連接到當前頁面的那個頁面的 URL,不能夠設置

        //取得來源頁面的 URL
        var referrer = document.referrer;
    • 查找元素

      • getElementById()方法,接收一個參數:要取得的元素的 ID。若是找到相應的元素則返回該元素,若是不存在帶有相應 ID 的元素,則返回 null

        <div id="myDiv">Some text</div>
        <script>
          //可使用下面的代碼取得這個元素:
          var div = document.getElementById("myDiv"); //取得<div>元素的引用
        </script>
      • getElementsByTagName()方法,接受一個參數,即要取得元素的標籤名,而返回的是包含零或多個元素的 NodeList,這個方法會返回一個 HTMLCollection 對象,做爲一個「動態」集合

        //與 NodeList 對象相似的方法
        var images = document.getElementsByTagName("img");
        alert(images.length); //輸出圖像的數量
        alert(images[0].src); //輸出第一個圖像元素的 src 特性
        alert(images.item(0).src); //輸出第一個圖像元素的 src 特性
        
        //HTMLCollection 對象的方法
        //<img src="myimage.gif" name="myImage"> 
        var myImage = images.namedItem("myImage"); //namedItem
        var myImage = images["myImage"]; //按名稱訪問項
        
        //取得文檔中的全部元素
        var allElements = document.getElementsByTagName("*");
      • getElementsByName()方法,只有 HTMLDocument 類型纔有的方法,會返回帶有給定 name 特性的全部元素

        <fieldset>
          <legend>Which color do you prefer?</legend>
          <ul>
              <li><input type="radio" value="red" name="color" id="colorRed">
              <label for="colorRed">Red</label></li>
              <li><input type="radio" value="green" name="color"id="colorGreen">
              <label for="colorGreen">Green</label></li>
              <li><input type="radio" value="blue" name="color" id="colorBlue">
              <label for="colorBlue">Blue</label></li>
          </ul>
        </fieldset>
        <script>
            ///返回一個 HTMLCollectioin
          var radios = document.getElementsByName("color"); /
        </script>
    • 特殊集合:這些集合都是 HTMLCollection 對象,爲訪問文檔經常使用的部分提供了快捷方式

      • document.anchors,包含文檔中全部帶 name 特性的元素;
      • document.applets,包含文檔中全部的<applet>元素,再也不推薦使用了
      • document.forms,包含文檔中全部的<form>元素
      • document.images,包含文檔中全部的<img>元素
      • document.links,包含文檔中全部帶 href 特性的<a>元素
    • DOM 一致性檢測

      • 因爲 DOM 分爲多個級別,也包含多個部分,所以檢測瀏覽器實現了 DOM 的哪些部分就十分必要

      • document.implementation 屬性就是爲此提供相應信息和功能的對象,與瀏覽器對 DOM 的實現
        直接對應

      • DOM1 級只爲 document.implementation 規定了一個方法,即 hasFeature()方法,接受兩個參數:要檢測的 DOM 功能的名稱及版本號。若是瀏覽器支持給定名稱和版本的功能,則該方法返回 true

        var hasXmlDom = document.implementation.hasFeature("XML", "1.0");
      • 建議多數狀況下,在使用 DOM 的某些特殊的功能以前,最好除了檢測hasFeature()以外,還同時使用能力檢測

    • 文檔寫入

      • write()方法,接受一個字符串參數,即要寫入到輸出流中的文本,原樣寫入

        <html>
        <head>
          <title>document.write() Example</title>
        </head>
        <body>
          <p>The current date and time is:
          <script type="text/javascript">
              document.write("<strong>" + (new Date()).toString() + </strong>");
          </script>
          </p>
        </body>
        </html>
      • writeln()方法,接受一個字符串參數,即要寫入到輸出流中的文本,在字符串的末尾添加換行符

      • open()和 close()分別用於打開和關閉網頁的輸出流,若是是在頁面加載期間使用 write()
        或 writeln()方法,則不須要用到這兩個方法

  • Element類型

    • 概念:用於表現 XML 或 HTML元素,提供了對元素標籤名、子節點及特性的訪問

    • 特徵:

      • nodeType 的值爲 1;
      • nodeName 的值爲元素的標籤名;
      • nodeValue 的值爲 null;
      • parentNode 多是 Document 或 Element;
      • 其子節點多是 Element、Text、Comment、ProcessingInstruction、CDATASection 或EntityReference
      //訪問元素的標籤名,可使用 nodeName 屬性,也可使用 tagName 屬性
      var div = document.getElementById("myDiv");
      alert(div.tagName); //"DIV"
      alert(div.tagName == div.nodeName); //true 
      
      //這種作法適用於 HTML 文檔,也適用於 XML 文檔
      if (element.tagName.toLowerCase() == "div"){ //這樣最好(適用於任何文檔)
          //在此執行某些操做
      }
    • HTML 元素:全部 HTML 元素都由 HTMLElement 類型表示,具備下列標準特性

      • id,元素在文檔中的惟一標識符
      • title,有關元素的附加說明信息,通常經過工具提示條顯示出來
      • lang,元素內容的語言代碼,不多使用
      • dir,語言的方向,值爲"ltr"(left-to-right,從左至右)或"rtl"(right-to-left,從右至左)
      • className,與元素的class 特性對應,即爲元素指定的CSS類
      <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> 
      <script>
          var div = document.getElementById("myDiv");
          //獲取
          alert(div.id); //"myDiv""
          alert(div.className); //"bd"
          alert(div.title); //"Body text"
          alert(div.lang); //"en"
          alert(div.dir); //"ltr"
          //設置
          div.id = "someOtherId";
          div.className = "ft";
          div.title = "Some other text";
          div.lang = "fr";
          div.dir ="rtl"; 
      </script>
    • 取得特性:getAttribute()方法

      • 傳遞給 getAttribute()的特性名與實際的特性名相同

        var div = document.getElementById("myDiv");
        alert(div.getAttribute("id")); //"myDiv"
        alert(div.getAttribute("class")); //"bd"
        alert(div.getAttribute("title")); //"Body text"
        alert(div.getAttribute("lang")); //"en"
        alert(div.getAttribute("dir")); //"ltr"
      • 經過 getAttribute()方法也能夠取得自定義特性

        //<div id="myDiv" my_special_attribute="hello!"></div> 
        var value = div.getAttribute("my_special_attribute");
        //根據 HTML5 規範,自定義特性應該加上 data-前綴以便驗證
      • 只有公認的(非自定義的)特性纔會以屬性的形式添加到 DOM對象中

        //<div id="myDiv" align="left" my_special_attribute="hello!"></div> 
        alert(div.id); //"myDiv"
        alert(div.my_special_attribute); //undefined(IE 除外)
        alert(div.align); //"left"
      • style屬性,用於經過 CSS 爲元素指定樣式。在經過 getAttribute()訪問時,返回的 style 特性值中包含的是 CSS 文本,而經過屬性來訪問它則會返回一個對象

      • onclick 事件處理程序,當在元素上使用時,onclick 特性中包含的是 JavaScript 代碼,若是經過 getAttribute()訪問,則會返回相應代碼的字符串。而在訪問onclick 屬性時,則會返回一個 JavaScript 函數(若是未在元素中指定相應特性,則返回 null)。這是由於 onclick 及其餘事件處理程序屬性自己就應該被賦予函數值

      • 因爲存在這些差異,在經過 JavaScript 以編程方式操做 DOM 時,開發人員常常不使用getAttribute(),而是隻使用對象的屬性。只有在取得自定義特性值的狀況下,纔會使用getAttribute()方法

    • 設置特性:setAttribute()方法

      • 接受兩個參數:要設置的特性名和值。若是特性已經存在,setAttribute()會以指定的值替換現有的值;若是特性不存在,setAttribute()則建立該屬性並設置相應的值

        div.setAttribute("id", "someOtherId");
        div.setAttribute("class", "ft");
        div.setAttribute("title", "Some other text");
        div.setAttribute("lang","fr");
        div.setAttribute("dir", "rtl");
      • 經過 setAttribute()方法既能夠操做 HTML 特性也能夠操做自定義特性

      • 由於全部特性都是屬性,因此直接給屬性賦值能夠設置特性的值

        //公認的(非自定義的)特性
        div.id = "someOtherId";
        div.align = "left"; 
        //自定義的屬性,該屬性不會自動成爲元素的特性
        div.mycolor = "red";
        alert(div.getAttribute("mycolor")); //null(IE 除外)
    • 移除屬性:是 removeAttribute()方法

      • 用於完全刪除元素的特性,不只會清除特性的值,並且也會從元素中徹底刪除特性

        div.removeAttribute("class");
      • 這個方法並不經常使用,但在序列化 DOM 元素時,能夠經過它來確切地指定要包含哪些特性

    • attributes 屬性:attributes 屬性中包含一個NamedNodeMap,與 NodeList 相似

    • getNamedItem(name)方法:返回 nodeName 屬性等於 name 的節點。attributes 屬性中包含一系列節點,每一個節點的 nodeName 就是特性的名稱,而節點的 nodeValue就是特性的值

      var id = element.attributes.getNamedItem("id").nodeValue; 
      var id = element.attributes["id"].nodeValue; //簡寫
      element.attributes["id"].nodeValue = "someOtherId"; //設置特性的值
    • removeNamedItem(name)方法:從列表中移除 nodeName 屬性等於 name 的節點。直接刪除具備給定名稱的特性,返回表示被刪除特性的 Attr 節點

      var oldAttr = element.attributes.removeNamedItem("id");
    • setNamedItem(node)方法:向列表中添加節點,以節點的 nodeName 屬性爲索引

      element.attributes.setNamedItem(newAttr);
    • item(pos):返回位於數字 pos 位置處的節點

    • 使用attributes 屬性遍歷元素的特性,在須要將 DOM 結構序列化爲 XML 或 HTML 字符串時,多數都會涉及遍歷元素特性

      function outputAttributes(element){
        var pairs = new Array(),
        attrName,
        attrValue,
        i,
        len;
        for (i=0, len=element.attributes.length; i < len; i++){
            attrName = element.attributes[i].nodeName;
            attrValue = element.attributes[i].nodeValue;
            if (element.attributes[i].specified) {
                pairs.push(attrName + "=\"" + attrValue + "\"");
            }
        }
        return pairs.join(" ");
      }
    • 建立元素

      • 使用 document.createElement()方法能夠建立新元素,只接受一個參數,即要建立元素的標籤名

        var div = document.createElement("div"); //建立
        div.id = "myNewDiv";                    
        div.className = "box"; 
        document.body.appendChild(div); //添加到文檔
      • IE 中能夠以另外一種方式使用 createElement(),即爲這個方法傳入完整的元素標籤,也能夠包含屬性

      var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");
      • 存在的問題:

        • 不能設置動態建立的<iframe>元素的 name 特性
        • 不能經過表單的 reset()方法重設動態建立的<input>元素
        • 動態建立的 type 特性值爲"reset"的<buttou>元素重設不了表單
        • 動態建立的一批 name 相同的單選按鈕彼此毫無關係。name 值相同的一組單選按鈕原本應該用
          於表示同一選項的不一樣值,但動態建立的一批這種單選按鈕之間卻沒有這種關係
        if (client.browser.ie && client.browser.ie <=7){
          //建立一個帶 name 特性的 iframe 元素
         var iframe = document.createElement("<iframe name=\"myframe\"</iframe>");
         //建立 input 元素
         var input = document.createElement("<input type=\"checkbox\">");
         //建立 button 元素
         var button = document.createElement("<button type=\"reset\"></button>");
         //建立單選按鈕
         var radio1 = document.createElement("<input type=\"radio\"name=\"choice\" "+"value=\"1\">");
         var radio2 = document.createElement("<input type=\"radio\"name=\"choice\" "+"value=\"2\">");
        }
    • 元素的子節點

      • 元素能夠有任意數目的子節點和後代節點,由於元素能夠是其餘元素的子節點

      • 元素的childNodes 屬性中包含了它的全部子節點,這些子節點有多是元素、文本節點、註釋或處理指令

      • 元素也支持getElementsByTagName()方法。在經過元素調用這個方法時,除了搜索起點是當前元素以外,其餘方面都跟經過 document 調用這個方法相同,所以結果只會返回當前元素的後代

        var ul = document.getElementById("myList");
        var items = ul.getElementsByTagName("li");
  • Text類型

    • 概念:文本節點由 Text 類型表示,包含的是能夠照字面解釋的純文本內容。純文本中能夠包含轉義後的HTML 字符,但不能包含 HTML 代碼

    • 特徵:

      • nodeType 的值爲 3
      • nodeName 的值爲"#text"
      • nodeValue 的值爲節點所包含的文本
      • parentNode 是一個 Element
      • 不支持(沒有)子節點
    • 操做:能夠經過 nodeValue 屬性或 data 屬性訪問 Text 節點中包含的文本,這兩個屬性中包含的值相同;對 nodeValue 的修改也會經過 data 反映出來,反之亦然。使用下列方法能夠操做節點中的文本:

      • appendData(text):將 text 添加到節點的末尾

      • deleteData(offset, count):從 offset 指定的位置開始刪除 count 個字符

      • insertData(offset, text):在 offset 指定的位置插入 text

      • replaceData(offset, count, text):用text替換從offset指定的位置開始到offset+count爲止處的文本

      • splitText(offset):從 offset 指定的位置將當前文本節點分紅兩個文本節點

      • substringData(offset, count):提取從 offset 指定的位置開始到 offset+count 爲止處的字符串

      • length 屬性,保存着節點中字符的數目,nodeValue.length 和 data.length 中也保存着一樣的值

      • 在默認狀況下,每一個能夠包含內容的元素最多隻能有一個文本節點,並且必須確實有內容存在

        <!-- 沒有內容,也就沒有文本節點 -->
        <div></div>
        <!-- 有空格,於是有一個文本節點 -->
        <div> </div>
        <!-- 有內容,於是有一個文本節點 -->
        <div>Hello World!</div> 
        <script>
          var textNode = div.firstChild; //或者 div.childNodes[0] 
            div.firstChild.nodeValue = "Some other message";
            //輸出結果是"Some &lt;strong&gt;other&lt;/strong&gt; message"
          div.firstChild.nodeValue = "Some <strong>other</strong> message";
        </script>
    • 建立文本節點

    • 使用document.createTextNode()建立新文本節點,這個方法接受一個參數:要插入節點中的文本;在建立新文本節點的同時,也會爲其設置 ownerDocument 屬性

      //
      var element = document.createElement("div");
      element.className = "message";
      var textNode = document.createTextNode("Hello world!");
      element.appendChild(textNode);
      document.body.appendChild(element); 
      
      //通常狀況下,每一個元素只有一個文本子節點,在某些狀況下也可能包含多個文本子節點
      //若是兩個文本節點是相鄰的同胞節點,那麼這兩個節點中的文本就會連起來顯示,中間不會有空格
      var element = document.createElement("div");
      element.className = "message";
      var textNode = document.createTextNode("Hello world!");
      element.appendChild(textNode);
      var anotherTextNode = document.createTextNode("Yippee!");
      element.appendChild(anotherTextNode);
      document.body.appendChild(element);
    • 通常狀況下,每一個元素只有一個文本子節點,在某些狀況下也可能包含多個文本子節點,若是兩個文本節點是相鄰的同胞節點,那麼這兩個節點中的文本就會連起來顯示,中間不會有空格

      var element = document.createElement("div");
      element.className = "message";
      var textNode = document.createTextNode("Hello world!");
      element.appendChild(textNode);
      var anotherTextNode = document.createTextNode("Yippee!");
      element.appendChild(anotherTextNode);
      document.body.appendChild(element);
    • 規範化文本節點

      • 在一個包含兩個或多個文本節點的父元素上調用 normalize()方法,則會將全部文本節點合併成一個
        節點,結果節點的 nodeValue 等於將合併前每一個文本節點的 nodeValue 值拼接起來的值

        var element = document.createElement("div");
        element.className = "message";
        var textNode = document.createTextNode("Hello world!");
        element.appendChild(textNode);
        var anotherTextNode = document.createTextNode("Yippee!");
        element.appendChild(anotherTextNode);
        document.body.appendChild(element);
        alert(element.childNodes.length); //2
        element.normalize();
        alert(element.childNodes.length); //1
        alert(element.firstChild.nodeValue); // "Hello world!Yippee!"
      • 瀏覽器在解析文檔時永遠不會建立相鄰的文本節點,這種狀況只會做爲執行 DOM 操做的結果出現

    • 分割文本節點

    • Text 類型提供了一個做用與 normalize()相反的方法:splitText(),這個方法會將一個文本節點分紅兩個文本節點,即按照指定的位置分割 nodeValue 值

    • 原來的文本節點將包含從開始到指定位置以前的內容,新文本節點將包含剩下的文本

    • 返回一個新文本節點,該節點與原節點的parentNode 相同

      var element = document.createElement("div");
      element.className = "message";
      var textNode = document.createTextNode("Hello world!");
      element.appendChild(textNode);
      document.body.appendChild(element);
      var newNode = element.firstChild.splitText(5);
      alert(element.firstChild.nodeValue); //"Hello"
      alert(newNode.nodeValue); //" world!"
      alert(element.childNodes.length); //2
  • Comment類型

    • 特徵:註釋在 DOM 中是經過 Comment 類型來表示的

      • nodeType 的值爲 8
      • nodeName 的值爲"#comment"
      • nodeValue 的值是註釋的內容
      • parentNode 多是 Document 或 Element
      • 不支持(沒有)子節點
    • Comment 類型與 Text 類型繼承自相同的基類,所以它擁有除 splitText()以外的全部字符串操做方法;與 Text 類型類似,也能夠經過 nodeValue 或 data 屬性來取得註釋的內容

      <div id="myDiv"><!--A comment --></div>
      <script>
          //註釋節點能夠經過其父節點來訪問
          var div = document.getElementById("myDiv");
          var comment = div.firstChild;
          alert(comment.data); //"A comment" 
          //使用 document.createComment()併爲其傳遞註釋文本也能夠建立註釋節點
          var comment = document.createComment("A comment "); 
      </script>
  • CDATASection類型

    • 概念:CDATASection 類型只針對基於 XML 的文檔,表示的是 CDATA 區域。與 Comment 相似,
      CDATASection 類型繼承自 Text 類型,所以擁有除 splitText()以外的全部字符串操做方法

    • 特徵:

      • nodeType 的值爲 4;
      • nodeName 的值爲"#cdata-section";
      • nodeValue 的值是 CDATA 區域中的內容;
      • parentNode 多是 Document 或 Element;
      • 不支持(沒有)子節點
    • CDATA 區域只會出如今 XML 文檔中,所以多數瀏覽器都會把 CDATA 區域錯誤地解析爲 Comment
      或 Element

      <div id="myDiv"><![CDATA[This is some content.]]></div>
    • 在真正的 XML 文檔中,可使用 document.createCDataSection()來建立 CDATA 區域,只需爲其傳入節點的內容便可

  • DocumentType類型

    • 特徵:包含着與文檔的 doctype 有關的全部信息

      • nodeType 的值爲 10;
      • nodeName 的值爲 doctype 的名稱;
      • nodeValue 的值爲 null;
      • parentNode 是 Document;
      • 不支持(沒有)子節點
    • 在 DOM1 級中,DocumentType 對象不能動態建立,而只能經過解析文檔代碼的方式來建立。持它的瀏覽器會把 DocumentType 對象保存在 document.doctype 中 。 DOM1 級描述了DocumentType 對象的 3 個屬性:

      • name 表示文檔類型的名稱
      • entities 是由文檔類型描述的實體的 NamedNodeMap 對象
      • notations 是由文檔類型描述的符號的NamedNodeMap 對象
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
      "http://www.w3.org/TR/html4/strict.dtd"> 
      <script>
          alert(document.doctype.name); //"HTML"
      </script>
  • DocumentFragment類型

    • 概念:在全部節點類型中,只有 DocumentFragment 在文檔中沒有對應的標記。DOM 規定文檔片斷
      (document fragment)是一種「輕量級」的文檔,能夠包含和控制節點,但不會像完整的文檔那樣佔用
      額外的資源。

    • 特徵:

      • nodeType 的值爲 11;
      • nodeName 的值爲"#document-fragment";
      • nodeValue 的值爲 null;
      • parentNode 的值爲 null;
      • 子節點能夠是 Element、ProcessingInstruction、Comment、Text、CDATASection 或
        EntityReference
    • 使用:不能把文檔片斷直接添加到文檔中,但能夠將它做爲一個「倉庫」來使用,便可以在裏面保存未來可能會添加到文檔中的節點。要建立文檔片斷,可使用document.createDocumentFragment()方法

      var fragment = document.createDocumentFragment();
    • 文檔片斷繼承了 Node 的全部方法,一般用於執行那些針對文檔的 DOM 操做

      <ul id="myList"></ul> 
      <script>
          var fragment = document.createDocumentFragment();
          var ul = document.getElementById("myList");
          var li = null;
          for (var i=0; i < 3; i++){
              li = document.createElement("li");
              li.appendChild(document.createTextNode("Item " + (i+1)));
              fragment.appendChild(li);
          }
          ul.appendChild(fragment); 
      </script>
  • Attr類型

    • 概念:元素的特性在 DOM 中以 Attr 類型來表示。在全部瀏覽器中(包括 IE8),均可以訪問 Attr 類型
      的構造函數和原型。從技術角度講,特性就是存在於元素的 attributes 屬性中的節點

    • 特徵:

      • nodeType 的值爲 2;
      • nodeName 的值是特性的名稱;
      • nodeValue 的值是特性的值;
      • parentNode 的值爲 null;
      • 在 HTML 中不支持(沒有)子節點;
      • 在 XML 中子節點能夠是 Text 或 EntityReference
    • 方法:儘管它們也是節點,但特性卻不被認爲是 DOM 文檔樹的一部分。開發人員最常使用的是 getAttribute()、setAttribute()和 remveAttribute()方法,不多直接引用特性節點

    • 屬性:Attr 對象有 3 個屬性:name、value 和 specified。其中,name 是特性名稱(與 nodeName 的
      值相同),value 是特性的值(與 nodeValue 的值相同),而 specified 是一個布爾值,用以區別特性是在代碼中指定的,仍是默認的

    • 使用 document.createAttribute()並傳入特性的名稱能夠建立新的特性節點

      var attr = document.createAttribute("align");
      attr.value = "left";
      element.setAttributeNode(attr);
      alert(element.attributes["align"].value); //"left"
      alert(element.getAttributeNode("align").value); //"left"
      alert(element.getAttribute("align")); //"left"

二、DOM操做技術

  • 動態腳本

    • 動態加載的外部 JavaScript 文件可以當即運行

      //<script type="text/javascript" src="client.js"></script> 
      function loadScript(url){
       var script = document.createElement("script");
       script.type = "text/javascript";
       script.src = url;
       document.body.appendChild(script);
      } 
      loadScript("client.js");
    • 指定 JavaScript 代碼的方式是行內方式

      //<script type="text/javascript">function sayHi(){alert("hi");}</script>
      function loadScriptString(code){
          var script = document.createElement("script");
          script.type = "text/javascript";
          try {
              script.appendChild(document.createTextNode(code));
          } catch (ex){
               script.text = code;
          }
          document.body.appendChild(script);
      }
      loadScriptString("function sayHi(){alert('hi');}");
  • 動態樣式

    • 使用<link>元素用於包含來自外部的文件

      //<link rel="stylesheet" type="text/css" href="styles.css">
      function loadStyles(url){
          var link = document.createElement("link");
          link.rel = "stylesheet";
          link.type = "text/css";
          link.href = url;
          var head = document.getElementsByTagName("head")[0];
          head.appendChild(link);
      } 
      loadStyles("styles.css");
    • 使用<style>元素用於包含嵌入式 CSS

      function loadStyleString(css){
          var style = document.createElement("style"); 
          style.type = "text/css";
          try{
              style.appendChild(document.createTextNode(css));
          } catch (ex){
              style.styleSheet.cssText = css;
          }
          var head = document.getElementsByTagName("head")[0];
          head.appendChild(style);
      }
      loadStyleString("body{background-color:red}");
    • 若是專門針對 IE 編寫代碼,務必當心使用 styleSheet.cssText 屬性。在重用同一個<style>元素並再次設置這個屬性時,有可能會致使瀏覽器崩潰。一樣,將cssText 屬性設置爲空字符串也可能致使瀏覽器崩潰。咱們但願 IE 中的這個 bug 可以在未來被修復

  • 操做表格

    • 傳統方法:使用 DOM 來建立下面的 HTML 表格

      //建立 table
      var table = document.createElement("table");
      table.border = 1;
      table.width = "100%";
      //建立 tbody
      var tbody = document.createElement("tbody"); 
      table.appendChild(tbody);
      //建立第一行
      var row1 = document.createElement("tr");
      tbody.appendChild(row1);
      var cell1_1 = document.createElement("td");
      cell1_1.appendChild(document.createTextNode("Cell 1,1"));
      row1.appendChild(cell1_1);
      var cell2_1 = document.createElement("td");
      cell2_1.appendChild(document.createTextNode("Cell 2,1"));
      row1.appendChild(cell2_1);
      //建立第二行
      var row2 = document.createElement("tr");
      tbody.appendChild(row2);
      var cell1_2 = document.createElement("td");
      cell1_2.appendChild(document.createTextNode("Cell 1,2"));
      row2.appendChild(cell1_2);
      var cell2_2= document.createElement("td");
      cell2_2.appendChild(document.createTextNode("Cell 2,2"));
      row2.appendChild(cell2_2);
      //將表格添加到文檔主體中
      document.body.appendChild(table);
    • HTML DOM 還爲<table>添加了一些屬性和方法

      • caption:保存着對元素(若是有)的指針
      • tBodies:是一個元素的 HTMLCollection
      • tFoot:保存着對元素(若是有)的指針
      • tHead:保存着對元素(若是有)的指針
      • rows:是一個表格中全部行的 HTMLCollection
      • createTHead():建立元素,將其放到表格中,返回引用
      • createTFoot():建立元素,將其放到表格中,返回引用
      • createCaption():建立元素,將其放到表格中,返回引用
      • deleteTHead():刪除元素
      • deleteTFoot():刪除元素
      • deleteCaption():刪除元素
      • deleteRow(pos):刪除指定位置的行
      • insertRow(pos):向 rows 集合中的指定位置插入一行
    • HTML DOM 還爲<tbody>添加了一些屬性和方法

      • rows:保存着元素中行的 HTMLCollection
      • deleteRow(pos):刪除指定位置的行
      • insertRow(pos):向 rows 集合中的指定位置插入一行,返回對新插入行的引用
    • HTML DOM 還爲<tr>添加了一些屬性和方法

      • cells:保存着元素中單元格的 HTMLCollection
      • deleteCell(pos):刪除指定位置的單元格
      • insertCell(pos):向 cells 集合中的指定位置插入一個單元格,返回對新插入單元格的引用
    • 使用新方法建立表格

      //建立 table
      var table = document.createElement("table");
      table.border = 1;
      table.width = "100%";
      //建立 tbody
      var tbody = document.createElement("tbody");
      table.appendChild(tbody);
      //建立第一行
      tbody.insertRow(0);
      tbody.rows[0].insertCell(0);
      tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"));
      tbody.rows[0].insertCell(1);
      tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
      //建立第二行
      tbody.insertRow(1);
      tbody.rows[1].insertCell(0);
      tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
      tbody.rows[1].insertCell(1);
      tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
      //將表格添加到文檔主體中
      document.body.appendChild(table);
  • 使用NodeList

  • 全部 NodeList 對象都是在訪問 DOM 文檔時實時運行的查詢

    var divs = document.getElementsByTagName("div"),
     i,
     div;
    for (i=0; i < divs.length; i++){
        div = document.createElement("div");
        document.body.appendChild(div);
    }
  • 若是想要迭代一個NodeList,最好是使用length屬性初始化第二個變量,而後將迭代器與該變量進行比較

    var divs = document.getElementsByTagName("div"),
     i,
     len,
     div;
    for (i=0, len=divs.length; i < len; i++){
        div = document.createElement("div");
        document.body.appendChild(div);
    }
  • 通常來講,應該儘可能減小訪問 NodeList 的次數。由於每次訪問 NodeList,都會運行一次基於文檔的查詢。因此,能夠考慮將從 NodeList 中取得的值緩存起來

三、總結

  • DOM 是語言中立的 API,用於訪問和操做 HTML 和 XML 文檔。DOM1 級將 HTML 和 XML 文檔形象地看做一個層次化的節點樹,可使用 JavaScript 來操做這個節點樹,進而改變底層文檔的外觀和結構;DOM 由各類節點構成,簡要總結以下:
    • 最基本的節點類型是 Node,用於抽象地表示文檔中一個獨立的部分;全部其餘類型都繼承自Node
    • Document 類型表示整個文檔,是一組分層節點的根節點。在 JavaScript 中,document 對象是
      Document 的一個實例。使用 document 對象,有不少種方式能夠查詢和取得節點
    • Element 節點表示文檔中的全部 HTML 或 XML 元素,能夠用來操做這些元素的內容和特性。
    • 另外還有一些節點類型,分別表示文本內容、註釋、文檔類型、CDATA 區域和文檔片斷
  • 訪問 DOM 的操做在多數狀況下都很直觀,不過在處理<script><style>元素時仍是存在一些複雜性。因爲這兩個元素分別包含腳本和樣式信息,所以瀏覽器一般會將它們與其餘元素區別對待。這些區別致使了在針對這些元素使用 innerHTML 時,以及在建立新元素時的一些問題
  • 理解 DOM 的關鍵,就是理解 DOM 對性能的影響。DOM 操做每每是 JavaScript 程序中開銷最大的部分,而因訪問 NodeList 致使的問題爲最多。NodeList 對象都是「動態的」,這就意味着每次訪問NodeList 對象,都會運行一次查詢。有鑑於此,最好的辦法就是儘可能減小 DOM 操做
相關文章
相關標籤/搜索