DOM擴展

一、選擇符API

  • querySelector()方法css

    • 接收一個 CSS 選擇符,返回與該模式匹配的第一個元素,若是沒有找到匹配的元素,返回 nullhtml

      //取得 body 元素
      var body = document.querySelector("body");
      //取得 ID 爲"myDiv"的元素
      var myDiv = document.querySelector("#myDiv");
      //取得類爲"selected"的第一個元素
      var selected = document.querySelector(".selected");
      //取得類爲"button"的第一個圖像元素
      var img = document.body.querySelector("img.button");
    • 經過 Document 類型調用 querySelector()方法時,會在文檔元素的範圍內查找匹配的元素。而經過 Element 類型調用 querySelector()方法時,只會在該元素後代元素的範圍內查找匹配的元素html5

    • 若是傳入了不被支持的選擇符,querySelector()會拋出錯誤node

  • querySelectorAll()方法web

    • 接收一個 CSS 選擇符,返回的一個 NodeList 的實例,包含匹配的全部元素,若是沒有找到匹配的元素,NodeList 就是空的api

    • 可以調用 querySelectorAll()方法的類型包括 Document、DocumentFragment 和 Element瀏覽器

      //取得某<div>中的全部<em>元素(相似於 getElementsByTagName("em"))
      var ems = document.getElementById("myDiv").querySelectorAll("em");
      //取得類爲"selected"的全部元素
      var selecteds = document.querySelectorAll(".selected");
      //取得全部<p>元素中的全部<strong>元素
      var strongs = document.querySelectorAll("p strong");
    • 要取得返回的 NodeList 中的每個元素,可使用 item()方法,也可使用方括號語法安全

      var i, len, strong;
      for (i=0, len=strongs.length; i < len; i++){
          strong = strongs[i]; //或者 strongs.item(i)
          strong.className = "important";
      }
    • 若是傳入了瀏覽器不支持的選擇符或者選擇符中有語法錯誤,querySelectorAll()會拋出錯誤app

  • matchesSelector()方法性能

    • 接收一個參數,即 CSS 選擇符,若是調用元素與該選擇符匹配,返回 true;不然,返回 false

      if (document.body.matchesSelector("body.page1")){
          //true
      }
    • 兼容性:

      • IE 9+經過 msMatchesSelector()支持該方法
      • Firefox 3.6+經過 mozMatchesSelector()支持該方法
      • Safari 5+和 Chrome 經過 webkitMatchesSelector()支持該方法
      function matchesSelector(element, selector){
       if (element.matchesSelector){
          return element.matchesSelector(selector);
       } else if (element.msMatchesSelector){
          return element.msMatchesSelector(selector);
       } else if (element.mozMatchesSelector){
          return element.mozMatchesSelector(selector);
       } else if (element.webkitMatchesSelector){
          return element.webkitMatchesSelector(selector);
       } else {
          throw new Error("Not supported.");
       }
      }
      if (matchesSelector(document.body, "body.page1")){
          //執行操做
      }

二、元素遍歷

  • childElementCount:返回子元素(不包括文本節點和註釋)的個數

  • firstElementChild:指向第一個子元素;firstChild 的元素版

  • lastElementChild:指向最後一個子元素;lastChild 的元素版

  • previousElementSibling:指向前一個同輩元素;previousSibling 的元素版

  • nextElementSibling:指向後一個同輩元素;nextSibling 的元素版

    //跨瀏覽器遍歷某元素的全部子元素的通常方法
    var i,
    len,
    child = element.firstChild;
    while(child != element.lastChild){
      if (child.nodeType == 1){ //檢查是否是元素
          processChild(child);
      }
      child = child.nextSibling;
    } 
    //而使用 Element Traversal 新增的元素
    var i,
       len,
       child = element.firstElementChild;
      while(child != element.lastElementChild){
          processChild(child); //已知其是元素
          child = child.nextElementSibling;
      }

三、HTML5

  • 與類相關的擴充

    • getElementsByClassName()方法:接收一個參數,即一個包含一或多個類名的字符串,返回帶有指定類的全部元素的 NodeList

      //取得全部類中包含"username"和"current"的元素,類名的前後順序無所謂
      var allCurrentUsernames = document.getElementsByClassName("username current");
      //取得 ID 爲"myDiv"的元素中帶有類名"selected"的全部元素
      var selected = document.getElementById("myDiv").getElementsByClassName("selected");
    • classList 屬性

      • 概念:一種操做類名的方式,可讓操做更簡單也更安全,那就是爲全部元素添加classList 屬性。這個 classList 屬性是新集合類型 DOMTokenList 的實例。與其餘 DOM 集合相似,DOMTokenList 有一個表示本身包含多少元素的 length 屬性,而要取得每一個元素可使用 item()方法,也可使用方括號語法

      • 方法:

        add(value):將給定的字符串值添加到列表中。若是值已經存在,就不添加了
        contains(value):表示列表中是否存在給定的值,若是存在則返回 true,不然返回 false
        remove(value):從列表中刪除給定的字符串
        toggle(value):若是列表中已經存在給定的值,刪除它;若是列表中沒有給定的值,添加它
      • 有了 classList 屬性,除非你須要所有刪除全部類名,或者徹底重寫元素的 class 屬性,不然也
        就用不到 className 屬性了

        //<div class="bd user disabled">...</div> 
        //刪除"disabled"類
        div.classList.remove("disabled");
        //添加"current"類
        div.classList.add("current");
        //切換"user"類
        div.classList.toggle("user");
        //肯定元素中是否包含既定的類名
        if (div.classList.contains("bd") && !div.classList.contains("disabled")){
          //執行操做
        )
        //迭代類名
        for (var i=0, len=div.classList.length; i < len; i++){
          doSomething(div.classList[i]);
        }
  • 焦點管理

    • document.activeElement 屬性:始終會引用 DOM 中當前得到了焦點的元素。元素得到焦點的方式有頁面加載、用戶輸入(一般是經過按 Tab 鍵)和在代碼中調用 focus()方法

      var button = document.getElementById("myButton");
      button.focus();
      alert(document.activeElement === button); //true
    • document.hasFocus()方法:用於肯定文檔是否得到了焦點

      var button = document.getElementById("myButton"); 
      button.focus();
      alert(document.hasFocus()); //true
  • HTMLDocument的變化

    • Document 的readyState 屬性:

      • loading,正在加載文檔;
      • complete,已經加載完文檔
      if (document.readyState == "complete"){
        //執行操做
      }
    • 兼容模式:document 添加了一個名爲 compatMode 的屬性,爲了告訴開發人員瀏覽器採用了哪一種渲染模式

      if (document.compatMode == "CSS1Compat"){
          alert("Standards mode");//標準模式下,值等於"CSS1Compat"
      } else {
          alert("Quirks mode");//混雜模式下,值等於"BackCompat"。
      }
    • head 屬性:做爲對 document.body 引用文檔的元素的補充,引用文檔的元素

    var head = document.head || document.getElementsByTagName("head")[0];
  • 字符集屬性

    • charset 屬性:表示文檔中實際使用的字符集,也能夠用來指定新字符集。默認狀況下,這個屬性的值爲"UTF-16",但能夠經過元素、響應頭部或直接設置 charset 屬性修改這個值

      alert(document.charset); //"UTF-16"
      document.charset = "UTF-8";
    • defaultCharset屬性:表示根據默認瀏覽器及操做系統的設置,當前文檔默認的字符集應該是什麼。若是文檔沒有使用默認的字符集,那 charset 和 defaultCharset 屬性的值可能會不同

      if (document.charset != document.defaultCharset){
          alert("Custom character set being used.");
      }
  • 自定義數據屬性

    • HTML5 規定能夠爲元素添加非標準的屬性,但要添加前綴 data-,這些屬性能夠任意添加、隨便命名,只要以 data-開頭便可

      <div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
    • 添加了自定義屬性以後,能夠經過元素的 dataset 屬性來訪問自定義屬性的值。dataset 屬性的值是 DOMStringMap 的一個實例,也就是一個名值對兒的映射。在這個映射中,每一個 data-name 形式
      的屬性都會有一個對應的屬性,只不過屬性名沒有 data-前綴(好比,自定義屬性是 data-myname,
      那映射中對應的屬性就是 myname)

      //本例中使用的方法僅用於演示
      var div = document.getElementById("myDiv");
      //取得自定義屬性的值
      var appId = div.dataset.appId;
      var myName = div.dataset.myname;
      //設置值
      div.dataset.appId = 23456;
      div.dataset.myname = "Michael";
      //有沒有"myname"值呢?
      if (div.dataset.myname){ 
          alert("Hello, " + div.dataset.myname);
      }
  • 插入標記

    • innerHTML 屬性

      • 在讀模式下,innerHTML 屬性返回與調用元素的全部子節點(包括元素、註釋和文本節點)對應的 HTML 標記;

        <div id="content">
          <p>This is a <strong>paragraph</strong> with a list following it.</p>
          <ul>
              <li>Item 1</li>
              <li>Item 2</li>
              <li>Item 3</li>
          </ul>
        </div>
        對於上面的<div>元素來講,它的 innerHTML 屬性會返回以下字符串
        <p>This is a <strong>paragraph</strong> with a list following it.</p>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
          <li>Item 3</li>
        </ul>
      • 在寫模式下,innerHTML 會根據指定的值建立新的 DOM 樹,而後用這個 DOM 樹徹底替換調用元素原先的全部子節點

        div.innerHTML = "Hello world!"; 
        div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>"; 
        //結果:
        //<div id="content">Hello &amp; welcome, <b>&quot;reader&quot;!</b></div>
      • 不一樣瀏覽器返回的文本格式會有所不一樣。IE 和 Opera 會將全部標籤轉換爲大寫形式,而 Safari、Chrome 和 Firefox 則會原本來本地按照原先文檔中(或指定這些標籤時)的格式返回 HTML,包空格和縮進

      • 在大多數瀏覽器中,經過 innerHTML 插入<script>元素並不會執行其中的腳本

        div.innerHTML = "<script defer>alert('hi');<\/script>"; //無效
        //下面這幾行代碼均可以正常執行
        div.innerHTML="_<script defer>alert('hi');<\/script>";
        div.innerHTML="<div>&nbsp;</div><script defer>alert('hi');<\/script>";
        div.innerHTML="<input type=\"hidden\"><script defer>alert('hi')<\/script>";
      • 大多數瀏覽器都支持以直觀的方式經過 innerHTML 插入<style>元素

        div.innerHTML="<style type=\"text/css\">body{background-color:red;</style>"
        //IE8 及更早版本中
        div.innerHTML="_<style type=\"text/css\">body{background-color:red;</style>"
        div.removeChild(div.firstChild);
      • 並非全部元素都支持 innerHTML 屬性。不支持 innerHTML 的元素有:

        <col>、<colgroup>、<frameset>、<head>、<html>、<style>、<table>、<tbody>、<thead>、<tfoot>和<tr>
      • 不管何時,只要使用 innerHTML 從外部插入 HTML,都應該首先以可靠的方式處理 HTML。IE8 爲此提供了 window.toStaticHTML()方法,這個方法接收一個參數,即一個 HTML 字符串;返回一個通過無害處理後的版本——從源 HTML 中刪除全部腳本節點和事件處理程序屬性

        var text = "<a href=\"#\" onclick=\"alert('hi')\">Click Me</a>";
        var sanitized = window.toStaticHTML(text); //Internet Explorer 8 only
        alert(sanitized); //"<a href=\"#\">Click Me</a>"
    • outerHTML 屬性

      • 在讀模式下,outerHTML 返回調用它的元素及全部子節點的 HTML 標籤

        <div id="content">
          <p>This is a <strong>paragraph</strong> with a list following it.</p>
          <ul>
              <li>Item 1</li>
              <li>Item 2</li>
              <li>Item 3</li>
              </ul>
        </div> 
        對於上面的<div>元素來講,它的 outerHTML  屬性會返回以下字符串
        <div id="content">
          <p>This is a <strong>paragraph</strong> with a list following it.</p>
          <ul>
              <li>Item 1</li>
              <li>Item 2</li>
              <li>Item 3</li>
              </ul>
        </div>
      • 在寫模式下,outerHTML會根據指定的 HTML 字符串建立新的 DOM 子樹,而後用這個 DOM 子樹徹底替換調用元素。

        div.outerHTML = "<p>This is a paragraph.</p>"; 
        //這行代碼完成的操做與下面這些 DOM 腳本代碼同樣
        var p = document.createElement("p");
        p.appendChild(document.createTextNode("This is a paragraph."));
        div.parentNode.replaceChild(p, div);
    • insertAdjacentHTML()方法:它接收兩個參數,插入位置和要插入的 HTML 文本

      • 第一個參數必須是下列值之一(這些值都必須是小寫形式):

        "beforebegin",在當前元素以前插入一個緊鄰的同輩元素;
        "afterbegin",在當前元素之下插入一個新的子元素或在第一個子元素以前再插入新的子元素;
        "beforeend",在當前元素之下插入一個新的子元素或在最後一個子元素以後再插入新的子元素;
        "afterend",在當前元素以後插入一個緊鄰的同輩元素。
      • 第二個參數是一個 HTML 字符串(與 innerHTML 和 outerHTML的值相同),若是瀏覽器沒法解析該字符串,就會拋出錯誤

        //做爲前一個同輩元素插入
        element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");
        //做爲第一個子元素插入
        element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");
        //做爲最後一個子元素插入
        element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");
        //做爲後一個同輩元素插入
        element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");
    • 內存與性能問題

      • 在使用 innerHTML、outerHTML 屬性和 insertAdjacentHTML()方法時,最好先手工刪除要被替換的元素的全部事件處理程序和 JavaScript 對象屬性

      • 建立和銷燬 HTML 解析器也會帶來性能損失,因此最好可以將設置 innerHTML或 outerHTML 的次數控制在合理的範圍內

        var itemsHtml = "";
        for (var i=0, len=values.length; i < len; i++){
          itemsHtml += "<li>" + values[i] + "</li>";
        }
        ul.innerHTML = itemsHtml;
  • scrollIntoView()方法

    • scrollIntoView()能夠在全部 HTML 元素上調用,經過滾動瀏覽器窗口或某個容器元素,調用元素就能夠出如今視口中
    • 若是給這個方法傳入 true 做爲參數,或者不傳入任何參數,那麼窗口滾動以後會讓調用元素的頂部與視口頂部儘量平齊
    • 若是傳入 false 做爲參數,調用元素會盡量所有出如今視口中,(可能的話,調用元素的底部會與視口頂部平齊。)不過頂部不必定平齊
    //讓元素可見
    document.forms[0].scrollIntoView();

四、專有擴展

  • 文檔模式

    • 文檔模式決定了你可使用哪一個級別的 CSS,能夠在 JavaScript 中使用哪些 API,以及如何對待文檔類型(doctype)

    • 要強制瀏覽器以某種模式渲染頁面,可使用 HTTP 頭部信息X-UA-Compatible,或經過等價的標籤來設置

      <meta http-equiv="X-UA-Compatible" content="IE=IEVersion">
    • 默認狀況下,瀏覽器會經過文檔類型聲明來肯定是使用最佳的可用文檔模式,仍是使用混雜模式,經過 document.documentMode 屬性能夠知道給定頁面使用的是什麼文檔模式

      var mode = document.documentMode;
  • children屬性

    • 因爲 IE9 以前的版本與其餘瀏覽器在處理文本節點中的空白符時有差別,所以就出現了 children屬性

    • 這個屬性是 HTMLCollection 的實例,只包含元素中一樣仍是元素的子節點

    • 除此以外,children 屬性與 childNodes 沒有什麼區別,即在元素只包含元素子節點時,這兩個屬性的值相同

      var childCount = element.children.length;
      var firstChild = element.children[0];
  • contains()方法

    • 調用 contains()方法的應該是祖先節點,也就是搜索開始的節點,這個方法接收一個參數,即要檢測的後代節點。若是被檢測的節點是後代節點,該方法返回 true;不然,返回 false

      alert(document.documentElement.contains(document.body)); //true
    • compareDocumentPosition()也可以肯定節點間的關係,這個方法用於肯定兩個節點間的關係,返回一個表示該關係的位掩碼( bitmask)

    var result = document.documentElement.compareDocumentPosition(document.body);
    alert(!!(result & 16)); //16表明節點被包含(給定的節點是參考節點的後代)
    • 兼容性寫法

      function contains(refNode, otherNode){
          if (typeof refNode.contains == "function" &&
                      (!client.engine.webkit || client.engine.webkit >= 522)){
              return refNode.contains(otherNode);
          } else if (typeof refNode.compareDocumentPosition == "function"){
              return !!(refNode.compareDocumentPosition(otherNode) & 16);
          } else {
              var node = otherNode.parentNode;
              do {
                  if (node === refNode){
                          return true;
                  } else {
                          node = node.parentNode;
                  }
              } while (node !== null); 
                  return false;
          }
      }
  • 插入文本
    • innerText 屬性:能夠操做元素中包含的全部文本內容,包括子文檔樹中的文本

      • 在經過innerText 讀取值時,它會按照由淺入深的順序,將子文檔樹中的全部文本拼接起來

        <div id="content">
          <p>This is a <strong>paragraph</strong> with a list following it.</p>
          <ul>
              <li>Item 1</li>
              <li>Item 2</li>
              <li>Item 3</li>
          </ul>
        </div>
        對於這個例子中的<div>元素而言,其 innerText 屬性會返回下列字符串:
        This is a paragraph with a list following it.
        Item 1
        Item 2
        Item 3
      • 在經過innerText 寫入值時,結果會刪除元素的全部子節點,插入包含相應文本值的文本節點;也對文本中存在的 HTML 語法字符(小於號、大於號、引號及和號)進行了編碼

        div.innerText = "Hello & welcome, <b>\"reader\"!</b>"; 
        運行以上代碼以後,會獲得以下所示的結果
        <div id="content">
            Hello &amp;welcome,&lt;b&gt;&quot;reader&quot;!&lt;/b&gt;
        </div>
      • 設置 innerText 永遠只會生成當前節點的一個子文本節點,而爲了確保只生成一個子文本節點,就必需要對文本進行 HTML 編碼。利用這一點,能夠經過 innerText 屬性過濾掉 HTML 標籤。方法是將 innerText 設置爲等於 innerText,這樣就能夠去掉全部 HTML 標籤

        div.innerText = div.innerText;
      • 因爲不一樣瀏覽器處理空白符的方式不一樣,所以輸出的文本可能會也可能不會包含原始 HTML 代碼中的縮進

      • 兼容性:

        function getInnerText(element){
          return (typeof element.textContent == "string") ?
          element.textContent : element.innerText;
        }
        function setInnerText(element, text){
          if (typeof element.textContent == "string"){
              element.textContent = text;
          } else {
              element.innerText = text;
          }
        } 
        setInnerText(div, "Hello world!");
        alert(getInnerText(div)); //"Hello world!"
    • outerText 屬性

      • 在讀取文本值時,outerText 與 innerText 的結果徹底同樣。但在寫模式下,outerText 就徹底不
        同了:outerText 不僅是替換調用它的元素的子節點,而是會替換整個元素(包括子節點)

        div.outerText = "Hello world!"; 
        //這行代碼實際上至關於以下兩行代碼:
        var text = document.createTextNode("Hello world!");
        div.parentNode.replaceChild(text, div);
      • 本質上,新的文本節點會徹底取代調用 outerText 的元素。此後,該元素就從文檔中被刪除,沒法訪問

  • 滾動

    • 是對 HTMLElement 類型的擴展,所以在全部元素中均可以調用:
      • scrollIntoViewIfNeeded(alignCenter):只在當前元素在視口中不可見的狀況下,才滾動瀏覽器窗口或容器元素,最終讓它可見。若是當前元素在視口中可見,這個方法什麼也不作。若是將可選的 alignCenter 參數設置爲 true,則表示儘可能將元素顯示在視口中部(垂直方向)。Safari 和 Chrome 實現了這個方法
      • scrollByLines(lineCount):將元素的內容滾動指定的行高,lineCount 值能夠是正值,也能夠是負值。Safari 和 Chrome 實現了這個方法
      • scrollByPages(pageCount):將元素的內容滾動指定的頁面高度,具體高度由元素的高度決定。Safari 和 Chrome 實現了這個方法
    • scrollIntoView()和 scrollIntoViewIfNeeded()的做用對象是元素的容器,而 scrollByLines()和scrollByPages()影響的則是元素自身

      //將頁面主體滾動 5 行
      document.body.scrollByLines(5); 
      //在當前元素不可見的時候,讓它進入瀏覽器的視口
      document.images[0].scrollIntoViewIfNeeded();
      //將頁面主體往回滾動 1 頁
      document.body.scrollByPages(-1);
      因爲 scrollIntoView()是惟一一個全部瀏覽器都支持的方法,所以仍是這個方法最經常使用。

五、總結

  • 雖然 DOM 爲與 XML 及 HTML 文檔交互制定了一系列核心 API,但仍然有幾個規範對標準的 DOM進行了擴展。這些擴展中有不少原來是瀏覽器專有的,但後來成爲了事實標準,因而其餘瀏覽器也都提供了相同的實現。本章介紹的三個這方面的規範以下:
    • Selectors API,定義了兩個方法,讓開發人員可以基於 CSS 選擇符從 DOM 中取得元素,這兩個方法是 querySelector()和 querySelectorAll()
    • Element Traversal,爲 DOM 元素定義了額外的屬性,讓開發人員可以更方便地從一個元素跳到另外一個元素。之因此會出現這個擴展,是由於瀏覽器處理 DOM 元素間空白符的方式不同
    • HTML5,爲標準的 DOM 定義了不少擴展功能。其中包括在 innerHTML 屬性這樣的事實標準基礎上提供的標準定義,以及爲管理焦點、設置字符集、滾動頁面而規定的擴展 API
  • 雖然目前 DOM 擴展的數量還很少,但隨着 Web 技術的發展,相信必定還會涌現出更多擴展來。不少瀏覽器都在試驗專有的擴展,而這些擴展一旦得到承認,就能成爲「僞」標準,甚至會被收錄到規範的更新版本中
相關文章
相關標籤/搜索