下述内容主要講述了《JavaScript進階程式設計(第3版)》第11章關于“DOM擴充”。
盡管DOM作為API已經非常完善了,但是為了實作更多的功能,仍然會有一些标準或專有的擴充。對DOM的兩個主要擴充是Selectors API(選擇符API)和HTML5。
一、選擇符API
Selectors API是由W3C發起制定的一個标準,緻力于讓浏覽器原生支援CSS查詢。其核心的兩個方法:
querySelector()
和
querySelectorAll()
。
1. querySelector()方法
querySelector()
接受一個CSS選擇符,傳回與該模式比對的第一個元素,如果沒有找到比對的元素傳回null。
示例:
document.querySelector("body");
document.querySelector("#content");
document.querySelector(".bd");
複制
2. querySelectorAll()方法
querySelectorAll()接受一個CSS選擇符,傳回與該模式比對的NodeList執行個體,如果沒有找到比對的元素傳回null。
示例:
var lis = document.querySelectorAll("ul li");
// 擷取第一個li
lis.item(0); // 等價于:lis[0];
複制
注意:上述兩個函數如果傳入不支援的選擇符,會抛出錯誤。
3. matchesSelector()方法
matchesSelector()
方法大部分浏覽器未實作,可使用matches()或者使用各個浏覽器實驗性的實作。
function matchesSelector(element, selec)
複制
如果調用元素與該選擇符比對,傳回true;否則,傳回false。
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 if(ele.matches){
return ele.matches(selector);
} else{
throw new Error("Not supported.");
}
}
matchesSelector(document.body, "body"); // true
matchesSelector(document.body, "body div"); // false
複制

二、元素周遊
對于元素間的空格,各浏覽器對于childNodes和firstChild等屬性處理行為不一緻,為了彌補差異,Element Traversal新定義了一組屬性。
示例:
<p>李剛</p>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<p id="blog">http://blog.csdn.net/ligang2585116</p>
複制
屬性 | 說明 | 示例 |
---|---|---|
childElementCount | 子元素(不包含文本節點和注釋)的個數 | 5 |
firstElementChild | 指向第一個子元素,firstChild的元素版 | <li>Item 1</li> |
lastElementChild | 指向最後一個子元素,lastChild的元素版 | <li>Item 5</li> |
previousElementSibling | 指向前一個同輩元素,previousSibling的元素版 | <p>李剛</p> |
nextElementSibling | 指向後一個同輩元素,nextSibling的元素版 | <p id="blog">http://blog.csdn.net/ligang2585116</p> |
var ul = document.querySelector("ul");
ul.firstChild; // #text
ul.lastChild; // <li>Item 5</li>
ul.previousSibling; // #text
ul.nextSibling; // #text
複制
三、HTML5
1. 與類相關的擴充
(1)
getElementsByClassName("包含一或多個類名的字元串")
document.getElementsByClassName("bj"); // 取得類中包含“bj”的元素
document.getElementsByClassName("bj bd"); // 取得類中同時包含“bj”和“bd”的元素
複制
(2)classList屬性
在操作類名時,需要通過className屬性添加、删除和替換類型名。因為className中是一個字元串,是以即使隻修改字元串的一部分,也必須每次都設定整個字元串的值。
<div class="bd user">ligang</div>
複制
示例:className方式
var div = document.getElementsByClassName("bd")[0];
var classNames = div.className.split(/\s+/);
// 查找要删除的類名"user"
var index = classNames.indexOf("user");
// 删除類名"user"
classNames.splice(index, 1);
// 重新設定className
div.className = classNames;
複制
示例:classList方式
var div = document.getElementsByClassName("bd")[0];
div.classList.remove("user");
複制
classList屬性是DOMTokenList的執行個體,其含有length屬性。
屬性 | 說明 |
---|---|
add(value) | 将給定的字元串添加到清單中。如果已存在,就不添加了 |
contains(value) | 表示清單中是否存在給定的值 |
remove(value) | 從清單中删除給定的字元串 |
toggle(value) | 如果清單中已存在給定的值,删除它;如果不存在,添加它 |
有了classList屬性,除非需要删除全部所有類名,或者完全重寫元素的class屬性,否則也就用不到className屬性了。
2. 焦點管理
document.activeElement
屬性始終會引用DOM中目前獲得了焦點的元素。
示例:
var btn = document.getElementById("myBtn");
btn.focus();
console.log(btn.hasFocus()); // true
console.log(document.activeElement === btn); // true
複制
預設情況下,文檔剛剛加載完成時,
document.activeElement
中儲存的是
document.body
元素的引用。文檔加載期間,
document.activeElement
為null。
查詢文檔獲知哪個元素獲得了焦點,以及确定文檔是否獲得了焦點,這兩個功能最重要的用途是提高Web應用的無障礙性。無障礙Web應用的一個重要标志就是恰當的焦點管理,而确切地知道哪個元素獲得了焦點是一個極大的進步。
3. HTMLDocument的變化
(1)readyState屬性
- loading:正在加載文檔;
-
complete:已經加載完文檔。
使用document.readyState的最恰當方式,就是通過它來實作一個訓示文檔已經加載完成的訓示器。
(2)相容模式
document.compatMode
告知開發人員浏覽器采用了哪種渲染模式。
- CSS1Compat:标準模式;
- BackCompat:混雜模式。
(3)head屬性
HTML5新增了
document.head
屬性,引用文檔的
<head>
元素。
var head = document.head || document.getElementsByTagName("head")[0];
複制
4. 字元集屬性
- document.charset:文檔中實際使用的字元集,也可以用來指定新字元集;
- document.defaultCharset:表示根據浏覽器及作業系統的設定,目前文檔預設的字元集應該是什麼,注意相容性。
5. 自定義資料屬性
HTML5規定可以為元素添加非标準的屬性,但要添加字首data-,目的是為元素提供與渲染無關的資訊,或者提供語義資訊。
<div id="div" data-status="1" data-appId="123" data-customName="ligang">哈哈</div>
複制
添加了自定義屬性後,可以通過元素的dataset屬性來通路自定義屬性的值。
var div = document.getElementById("div");
var dataset = div.dataset;
console.log(dataset.appid); // 123
console.log(dataset.customname); // "ligang" 注意,都是小寫
複制
6. 插入标記
DOM操作文檔插入HTML标記非常複雜,因為其不僅要建立一系列DOM節點,而且還要小心地按照正确順序進行連接配接。詳見:DOM
示例:建立a标簽并插入到body
var a = document.createElement("a");
a.href = "http://blog.csdn.net/ligang2585116";
a.text = "http://blog.csdn.net/ligang2585116";
document.body.appendChild(a);
複制
使用插入标記的技術,直接插入HTML字元串不僅簡單而且速度更快。
(1)innerHTML屬性
讀模式:傳回調用元素的所有節點(包括元素、注釋和文本節點)對應的HTML标記;
寫模式:根據指定的值建立新的DOM樹,然後用這個DOM樹完全替換調用元素原先的所有子節點。
示例:建立a标簽并插入到body
document.body.innerHTML += '<a href="http://blog.csdn.net/ligang2585116">http://blog.csdn.net/ligang2585116</a>'
複制
注意:上述使用“+=”,因為寫模式下,innerHTML的值會被解析為DOM子樹,替換調用元素原來的所有子節點。
說明:使用innerHTML插入的字元串開頭是一個無作用域的元素(
<script>、<style>
)時,IE會在解析這個字元串前先删除該元素。
div.innerHTML = "<script>console.logt('hi')</script>"; // 無效
div.innerHTML = "<div> </div><script>console.logt('hi')</script>"; // 有效
div.innerHTML = "<input type=\"hidden\"><script>console.logt('hi')</script>"; // 有效
複制
(2)outerHTML屬性
讀模式:傳回調用它的元素及所有節點(包括元素、注釋和文本節點)對應的HTML标記;
寫模式:根據指定的值建立新的DOM樹,然後用這個DOM樹完全替換調用元素。
示例:
<div id="blog">
<!-- blog address -->
<a href="http://blog.csdn.net/ligang2585116">blog</a>
</div>
複制
var div = document.getElementById("blog");
div.outerHTML; // 整個innerHTML内容+div本身
div.outerHTML = "<a href=\"http://blog.csdn.net/ligang2585116\">blog</a>"; // 整個div會被a标簽替換掉
// 等價于
var a = document.createElement("a");
a.href = "http://blog.csdn.net/ligang2585116";
a.text = "blog";
div.parentNode.replaceChild(a, div);
複制
(3)記憶體與性能問題
使用上述節點方法可能會導緻浏覽器的記憶體占用問題。在删除帶有事件處理程式或引用了其他JavaScript對象子樹時,就會導緻記憶體占用問題。在使用innerHTML、outerHTML等方法時,最好先删除要替換的元素的所有事件處理程式和JavaScript對象屬性。
7. scrollIntoView()方法
如何滾動頁面也是DOM規範沒有解決的一個問題,HTML5最終選擇了
scrollIntoView
作為标準。
scrollIntoView()
可以在任何元素上調用。預設或傳入true,視窗會滾動讓調用元素與視口頂部盡可能齊平;傳入false,調用元素盡可能會全部出現在視窗中。
document.forms[0].scrollIntoView();
複制
當頁面發生變化時,一般會用這個方法來吸引使用者的注意力。實際上,為某個元素設定焦點也會導緻浏覽器滾動并顯示出獲得焦點的元素。
四、專有擴充
1. children屬性
children屬性與childNodes沒有什麼差別。
2. contains()方法
判斷某個節點是不是另一個節點的後代。
document.body.contains(div); // true
複制
掩碼 | 節點關系 |
---|---|
1 | 無關(給定的節點不再目前文檔中) |
2 | 居前(給定的節點在DOM樹中位于參考節點之前) |
4 | 居後(給定的節點在DOM樹中位于參考節點之後) |
8 | 包含(給定的節點是參考節點的祖先) |
16 | 被包含(給定的節點是參考節點的後代) |
示例:通用的contains函數
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;
}
}
console.log(contains(document.documentElement, document.body)); // true
複制
3. 插入文本
innerText
和
outerText
并沒有被納入HTML5。
其對文本進行操作,使用方式類似于
innerHTM
和
outerHTML
。
4. 滾動
HTML5将scrollIntoView()方法納入規範之後,仍還有其他幾個專有方法可以在不同的浏覽器中使用。
(1)
scrollIntoViewIfNeeded(alignCenter)
:隻在目前元素視口中不可見的情況下,才滾動浏覽器視窗或容器元素,最終讓它可見。如果目前元素在視口中可見,這個方法什麼也不做。true,盡量将元素在顯示視口中部(垂直方向)。
(2)
scrollByLines(lineCount)
:将元素的内容滾動指定的行高。
(3)
scrollByPages(pageCount)
:将元素的内容滾動指定的頁面高度,具體高度由元素的高度決定。
注意:
scrollIntoView()
和
scrollIntoViewIfNeeded()
的作用對象是元素的容器;
scrollByLines()
和
scrollByPages()
影響的則是元素自身。