JavaScript之DOM擴展

DOM擴展

儘管現在DOM已經很完善了。但爲了實現更多、更復雜的功能,仍然會有一些標準或專有擴展被納入W3C標準中。現在DOM主要有兩個擴展:selector API(選擇符API)和HTML5。

選擇符API

在傳統的 JavaScript 開發中,查找 DOM 往往是開發人員遇到的第一個頭疼的問題,原生的 JavaScript 所提供的 DOM 選擇方法並不多,僅僅侷限於通過tag, name, id 等方式來查找,這顯然是遠遠不夠的,如果想要進行更爲精確的選擇不得不使用看起來非常繁瑣的正則表達式,或者使用某個庫。事實上,現在所有的瀏覽器廠商都提供了querySelectorquerySelectorAll 這兩個方法的支持,甚至就連微軟也派出了 IE 8 作爲支持這一特性的代表,querySelector 和 querySelectorAll 作爲查找 DOM 的又一途徑,極大地方便了開發者,使用它們,你可以像使用 CSS 選擇器一樣快速地查找到你需要的節點


querySelector()

該選擇符接收一個CSS選擇符,返回與該模式相匹配的 第一個元素(與該模式匹配的可能有多個元素,但只返回第一個),如果沒有匹配的元素,則返回null。它的查找方式與傳統的查找方式一樣。

比如:根據ID來選擇一個元素,先用傳統的方法獲取該元素:

var myDiv = document.getElementById("myDiv");

現在,使用querySelector()來獲取該元素:

var myDiv = document.querySelector("#myDiv");

通過元素本身,來獲取該元素,用傳統的方法:
var div = document.getElementsByTagName("div");

這樣可以獲取所有的div元素,但是想要這其中的第一個div,則需要這樣:
var div = document.getElementsByTagName("div")[0];

現在,使用querySelector()來獲取第一個與模式匹配的元素:
var div = document.querySelector("div")

同樣的,也可以獲取類名爲selected的第一個元素(因爲類名selected的元素可以有很多):
//取得類爲selected的第一個元素
var selected = document.querySelector(".selected");

注:通過Document類調用querySelector()方法時,會在文檔元素的範圍內查找匹配的元素。而通過Element類調用querySelector()調用時,會在當前元素的子元素範圍內查找匹配的元素。

querySelectorAll()

與querySelector()相類似的用法,querySelectorAll()是返回與模式相匹配的 所有元素。它返回的是一個nodeList類,類似數組一樣,如果沒有匹配的元素,則返回一個空的nodeList。比如,用document.getElementsByTagName()方法是返回與模式匹配的所有元素,通過querySelectorAll()也可以達到相同的效果。

注:querySelectorAll()返回的是帶有屬性和方法的NodeList,它的低層其實是DOM結構的一種快照,而不像NodeList是動態地查詢DOM結構,這樣就避免了使用NodeList引起的性能問題。

比如,頁面中有多個div元素,要獲取這所有的div元素,有兩種方法:
//第一種方法獲取所有的div元素
var div = document.getElementsByTagName("div");

//第二種方法獲取所有的div元素
var div = document.querySelectorAll("div");


在上述例子中,無法體現querySelector()querySelectorAll()的強大,但在一個複雜的頁面中,獲取更爲深層的某元素,用傳統的通過ID、TagName、name來獲取是 很複雜的,而通過querySelector()等來獲取就簡單很多。



元素遍歷


在DOM章節中,我們學過childNodes、firstChild以及lastChild這三個屬性,分別是獲取元素節點下的所有子節點、獲取元素節點下的第一個子節點、獲取元素節點下的最後一個子節點。但在這裏有一個問題,就是除了IE9及以前的瀏覽器,其它的主瀏覽器都會“承認”元素之間的空格,也就是說會返回空文本節點(瀏覽器將元素之間的空白看作是文本節點),這是一個不友好的事情。

例如:

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
    <ul id="ul_list">
      <li>PHP</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
  <script> 
      //通過ID獲取ul元素
      var ul = document.querySelector("#ul_list");
      //獲取ul下的所有子節點
      var childNodes = ul.childNodes;
      console.log(childNodes.length);
      //獲取ul下的第一個子節點
      var firstChild = ul.firstChild;
      console.log(firstChild.nodeName); //#text
      //獲取ul下的最後一個子節點
      var lastChild = ul.lastChild;
      console.log(lastChild.nodeName); //#text
  </script>  
  </body>  
</html>  



得出的結果與我們預想的有點不一樣,childNodes返回的ul元素節點的子節點數是7個(除了三個元素節點外,還有四個文本節點),就像是這樣的:



瀏覽器將元素之間的空白當成是文本節點,那麼就相當於ul元素有7個子節點。這與我們想要的不一樣。

而firstChild返回的節點的類型是"#text",返回的是第一個文本節點,與我們想要的元素節點“li”有差異。lastChild返回的也是一個文本節點,而不是li元素節點。


爲了彌補這一差異,Element Traversal API定義一組屬性

  • childElmentCount:返回子節點(不包括註釋節點和元素之間的文本節點)的個數。
  • firstElementChild:返回第一個子節點。是firstChild的元素版
  • lastElementChild:返回最後一個子節點。是lastChild的元素版
  • perviousElementSibling:指向前一個同輩節點,perviousChild的元素版
  • nextElementSibling:指向後一個同輩節點,nextChild的元素版

使用這些屬性來獲取子節點的:
<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
    <ul id="ul_list">
      <li>PHP</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
  <script> 
      //通過ID獲取ul元素
      var ul = document.querySelector("#ul_list");
      //獲取ul下的所有子節點
      var childNodes = ul.childElementCount;
      console.log(childNodes); //3

      //獲取ul下的第一個子節點
      var firstChild = ul.firstElementChild;
      console.log(firstChild.nodeName); //LI
      console.log(firstChild); 

      //獲取ul下的最後一個子節點
      var lastChild = ul.lastElementChild;
      console.log(lastChild.nodeName); //LI
      console.log(lastChild);
      
      //獲取前一個同輩節點
      var previousChild = ul.firstElementChild.previousElementSibling;
      console.log(previousChild); //null

      //獲取後一個同輩節點
      var nextChild = ul.lastElementChild.nextElementSibling;
      console.log(nextChild); //null 
  </script>  
  </body>  
</html>  





效果:




這樣就能得到我們想要的子節點元素了。




HTML5

與類相關的擴充

現在class類用的越來越多,那麼與類相關的擴充也得到了實現,有了新API對類的支持。


document.getElementsByClassName()

該屬性接收一個或多個類名字符串,返回帶有指定類的所有nodeList,也就是通過類名獲取元素,可以通過length屬性得到nodeList的數量。

該方法與getElementsByTagName()有性能問題,建議使用querySelectorAll()方法比較好。

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
    <div class="div1">div1</div>
    <div class="div1">div2</div>
  <script> 
      var div = document.getElementsByClassName("div1");
      div[0].style.backgroundColor = "red";
  </script>  
  </body>  
</html>  


效果:

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
    <div class="div1">div1</div>
    <div class="div1">div2</div>
  <script> 
      var div = document.querySelectorAll(".div1");
      div[0].style.backgroundColor = "red";
  </script>  
  </body>  
</html>  


效果一樣:



classList屬性

在操作類名時,可以通classNamet屬性來刪除、添加、和替換類名。
格式:elementNode.className

HTML5新增了一個classList屬性,該屬性可以調用如下方法來刪除類名、添加類名和替換類名:
  • add(value):將給定的類字符串添加到列表中,如果列表中已經存在該字符串,則不用添加了。
  • remove(value):將給定的類字符串從列表中刪除。
  • contains(value):確定列表中是否有給定的類字符串,有返回true,沒有返回false。
  • toggle(value):如果列表中沒有給定的類字符串,將它添加到列表中,如果列表中有給定的類字符串,則從列表中刪除它。
  • length:返回獲取元素類名的個數。
  • item(index):獲取指定索引的類名。
其中,value表示的是類名

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      .div1 {
        color: red;
      }
      .div2 {
        background-color: blue;
      }
      .div3 {
        color: yellow;
      }
    </style> 
  </head>  
  <body>  
    <div class="div1">測試classList屬性</div> <br>
    <input id="sub" type="submit" value="切換類名">
    
  <script> 
      var div = document.querySelector("div");
      var sub = document.querySelector("#sub");
      //刪除"div1"類
      div.classList.remove("div1");
    
      //添加"div2"類
      div.classList.add("div2");

      //檢查是否有"div3"這個類
      console.log(div.classList.contains("div3"));

      //通過點擊事件來切換"div3"類
      sub.onclick = function (event) {
          div.classList.toggle("div3");
          console.log(div.classList.contains("div3")); //時時判斷div是否有"div3"這個樣式
      };
  </script>  
  </body>  
</html>  



效果:



如果是刪除全部類名,或者重寫元素的class屬性,則需要用className屬性,否則用classList屬性是個很好的選擇。



焦點管理

HTML5k上也添加了輔助管理元素焦點的功能,首先就是document.activeElement屬性,這個屬性始終引用 DOM中 當前獲得焦點的元素

獲取焦點的方式有很多:頁面加載、用戶輸入和在代碼中調用focus()方法

document.hasFocus()確定文檔是否獲取焦點,來確定用戶是否與頁面進行交互。

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <input id="inp" type="text">
    
  <script> 
      var inp = document.querySelector("#inp");
      //當輸入框獲取焦點時,判斷activeElment是否引用了輸入框
      inp.onfocus = function () {
          console.log(document.activeElement === inp);
          console.log(document.hasFocus());
      }
  </script>  
  </body>  
</html>  


效果:




注:document.activeElement()和document.hasFocus()的最大用途在於:可以確切地知道哪個元素獲取了焦點,這樣就不能靠猜測了,這是一個很大的進步。



HTMLDocument的變化

readyState屬性
它有兩個值:
1、loading:正在加載
2、complete:已經加載完成

該屬性常用於文檔加載完成的指標器。

兼容模式
document.compatMode可以告訴開發人員瀏覽器的渲染方式是 標準模式渲染還是混雜模式渲染。

當document.compatMode == "CSS1Compat" 表示瀏覽器採用的是標準模式渲染。
當document.compaMode == "BackCompat" 表示瀏覽器採用的是混雜模式渲染。


head屬性
對<head>元素的引用,要麼用document.head,要麼用document.getElementsByTagName("head");
var head = document.head || document.getElementsByTagName("head")[0];

一個文檔中,只有一個<head>元素。


字符集屬性

charset表示文檔中實際用的字符編碼,document.charset就是對字符集屬性的引用。

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <input id="inp" type="text">
    
  <script> 
      //得到charset
      console.log(document.charset);
      //引用head元素
      console.log(document.head);
  </script>  
  </body>  
</html>  


效果:




自定義數據類型

HTML5規定可以爲元素添加非標準的屬性,非標準的屬性以"data-"開頭,目的是爲元素提供 與渲染無關 的信息或者提供語義信息。這些信息可以任意添加、命名,不影響渲染。

通過dataset可以獲取或設置自定義的非標準屬性的值。data-name在dataset中以名/值對形式存在的。

爲元素添加一些不可見的屬性以便其它處理,可以使用自定義數據類型

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">myDiv</div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
      
      //取得自定義的非標準屬性值
      var app = myDiv.dataset.app;
      var name = myDiv.dataset.name;
      console.log(app);
      console.log(name);

      //修改這些非標準屬性值
      myDiv.dataset.app = 999;
      myDiv.dataset.name = "bob";
      //修改後的非標準屬性值
      console.log(myDiv);
  </script>  
  </body>  
</html>  


效果:



插入標記

使用插入標記技術,直接插入HTML字符串不僅簡單、而且快。

innerHTML
在讀取模式下,該屬性返回 調用對象下的所有子節點內容(元素、註釋、文本節點,包括HTML標籤)。在寫入模式下,innerHTML根據寫入的值創建一個新的DOM樹,用於替換調用對象下的所有子節點

讀取模式:
<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">
      <p>innerHTML</p>
       <ul>
         <li>PHP</li> 
         <li>CSS</li> 
         <li>JavaScript</li> 
       </ul>	  
    </div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
	  console.log(myDiv.innerHTML);
  </script>  
  </body>  
</html>  


效果:





輸入模式:

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">
      <p>innerHTML</p>
       <ul>
         <li>PHP</li> 
         <li>CSS</li> 
         <li>JavaScript</li> 
       </ul>	  
    </div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
	  //輸入模式下,創建新的DOM來替換原所有子節點
	  myDiv.innerHTML = "<p>這是替換的文字</p>";
	  console.log(myDiv.innerHTML);
  </script>  
  </body>  
</html>  


效果:



這裏新創建的<p>元素的DOM節點替換了原<div>下所有子節點。但<div>元素還在。



outerHTML

在讀取模式下,該屬性返回 調用它的對象及以下所有子節點的內容(元素、註釋、文本節點,包括HTML標籤)的<html>標籤,在寫入模式下,outerHTML根據寫入的值創建一個新的DOM節點,用於完全替換調用對象。

讀取模式:

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">
      <p>innerHTML</p>
       <ul>
         <li>PHP</li> 
         <li>CSS</li> 
         <li>JavaScript</li> 
       </ul>	  
    </div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
	  //返回調用對象及以下所有子節點的內容(元素、註釋和文本節點,包括<html>標籤)的<html>標籤。
	  console.log(myDiv.outerHTML);
  </script>  
  </body>  
</html>  


效果:



outerHTML返回的包括了調用對象的<html>標籤。


寫入模式:

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">
      <p>innerHTML</p>
       <ul>
         <li>PHP</li> 
         <li>CSS</li> 
         <li>JavaScript</li> 
       </ul>	  
    </div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
	  
	  myDiv.outerHTML = "<p>替換文本</p>";
	 
	  console.log(document.body);
  </script>  
  </body>  
</html>  


<p>元素將<div>元素完全替換掉了。也就是說,頁面中沒有了<div>元素及以下的子節點,只有<p>元素了。


在使用innerHTML和outerHTML屬性時需要注意:在刪除或替換帶有事件處理程序的DOM結構中的子樹對象時(元素),由於事件觸發,該元素與事件處理程序已經綁定並存在於內存中了,當使用這兩個方法來替換或刪除這個元素(DOM結構中的子樹對象)時,只是刪除或替換了這個元素,但元素與事件處理程序的綁定關係還在內存中,並沒有刪除,所以這樣就白白浪費內存了,當所替換的元素越來越多時,佔用的內存也就越多。這樣就影響了性能,解決方法:最好手工刪除要被替換或刪除的元素的所有事處理程序和JavaScript對象屬性。


insertAdjacentHTML()

該方法接收兩個參數:插入的位置和要插入的HTML文本。

  • beforebegin:在當前節點之前插入一個緊臨的同輩節點
  • beforeend:在當前節點之下插入一個新的節點或在最後一個子節點之後插入新的節點
  • afterbegin:在當前節點之後插入一個緊臨的同輩節點
  • afterend:在當前節點之下插入一個新的節點或在第一個子節點之前插入新的節點

<!doctype html>  
<html lang="zh-en">  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title> 
    <style>
      
    </style> 
  </head>  
  <body>  

    <div id="myDiv" data-app="123" data-name="tom">
      <p>innerHTML</p>
       <ul>
         <li>PHP</li> 
         <li>CSS</li> 
         <li>JavaScript</li> 
       </ul>	  
    </div>
    
  <script> 
      var myDiv = document.querySelector("#myDiv");
	  //在指定節點前插入新節點
	  myDiv.insertAdjacentHTML("beforebegin", "<p>前面插入的文本</p>");
	  //在指定節點後面插入新節點
	  myDiv.insertAdjacentHTML("afterend", "<p>後面插入的文本</p>");
	  //在指定節點之下插入新節點
	  myDiv.insertAdjacentHTML("afterbegin", "<p>下面插入的文本1</p>");
	  myDiv.insertAdjacentHTML("beforeend", "<p>下面插入的文本2</p>");
	  
	  console.log(document.body);
	 
  </script>  
  </body>  
</html>  


效果:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章