天天看點

DOM擴充

下述内容主要講述了《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           

複制

DOM擴充

二、元素周遊

對于元素間的空格,各浏覽器對于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>&nbsp;</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,盡量将元素在顯示視口中部(垂直方向)。

DOM擴充

(2)

scrollByLines(lineCount)

:将元素的内容滾動指定的行高。

(3)

scrollByPages(pageCount)

:将元素的内容滾動指定的頁面高度,具體高度由元素的高度決定。

注意:

scrollIntoView()

scrollIntoViewIfNeeded()

的作用對象是元素的容器;

scrollByLines()

scrollByPages()

影響的則是元素自身。