選擇符API
- Selectors API(www.w3.org/TR/selectors-api/)是由W3C發起制定的一個標準,致力于讓瀏覽器原生支持CSS 查詢。
1、querySelector()方法
- querySelector()方法接收一個CSS選擇符,返回與該模式匹配的第一個元素,如果沒有找到匹配的元素,返回null。
//取得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");
2、querySelectorAll()方法
querySelectorAll()方法接收的參數也是一個CSS 選擇符,這個方法返回的是一個NodeList 的實例。
//取得某<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";
}
3、matchesSelector()方法
- matchesSelector()方法接收一個參數,即CSS選擇符,如果調用元素與該選擇符匹配,返回true;否則,返回false。
if (document.body.matchesSelector("body.page1")){
//true
}
IE 9+通過msMatchesSelector()支持該方法,Firefox3.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")){
//執行操作
}
元素遍歷
對于元素間的空格,IE9及之前版本不會返回文本節點,而其他所有瀏覽器都會返回文本節點。這樣,就導致了在使用childNodes 和firstChild 等屬性時的行為不一致。
為了彌補這一差異,而同時又保持DOM規范不變,ElementTraversal規范(www.w3.org/TR/ElementTraversal/)新定義了一組屬性。
-
Element Traversal API 為DOM元素添加了以下5 個屬性。
- childElementCount:返回子元素(不包括文本節點和注釋)的個數。
- firstElementChild:指向第一個子元素;firstChild 的元素版。
- lastElementChild:指向最后一個子元素;lastChild 的元素版。
- previousElementSibling:指向前一個同輩元素;previousSibling 的元素版。
- nextElementSibling:指向后一個同輩元素;nextSibling 的元素版。
利用這些元素不必擔心空白文本節點,從而可以更方便地查找DOM 元素。
/**要跨瀏覽器遍歷某元素的所有子元素**/
//過去
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;
}
- 支持Element Traversal 規范的瀏覽器有IE 9+、Firefox 3.5+、Safari 4+、Chrome 和Opera 10+。
HTML5
- HTML5 規范則圍繞如何使用新增標記定義了大量JavaScript API。其中一些API 與DOM 重疊,定義了瀏覽器應該支持的DOM擴展。
1、與類相關的擴充
(1)getElementsByClassName()方法、
//取得所有類中包含"username"和"current"的元素,類名的先后順序無所謂
var allCurrentUsernames = document.getElementsByClassName("username current");
//取得ID 為"myDiv"的元素中帶有類名"selected"的所有元素
var selected = document.getElementById("myDiv").getElementsByClassName("selected");
(2)classList 屬性
- 在操作類名時,需要通過className 屬性添加、刪除和替換類名。
<div class="bd user disabled">...</div>
- 要從中刪除一個類名,需要把這三個類名拆開,刪除不想要的那個,然后再把其他類名拼成一個新字符串。
//刪除"user"類
//首先,取得類名字符串并拆分成數組
var classNames = div.className.split(/\s+/);
//找到要刪的類名
var pos = -1,
i,
len;
for (i=0, len=classNames.length; i < len; i++){
if (classNames[i] == "user"){
pos = i;
break;
}
}
//刪除類名
classNames.splice(i,1);
//把剩下的類名拼成字符串并重新設置
div.className = classNames.join(" ");
HTML5 新增了一種操作類名的方式,可以讓操作更簡單也更安全,那就是為所有元素添加
classList 屬性。-
classList 屬性是新集合類型DOMTokenList的實例,具有包含多少元素的length屬性,取得每個元素的item()方法,以及下列方法。
- add(value):將給定的字符串值添加到列表中。如果值已經存在,就不添加了。
- contains(value):表示列表中是否存在給定的值,如果存在則返回true,否則返回false。
- remove(value):從列表中刪除給定的字符串。
- toggle(value):如果列表中已經存在給定的值,刪除它;如果列表中沒有給定的值,添加它。
極大地減少類似基本操作的復雜性
<div class="bd user disabled">...</div>
//刪除"user"類
div.classList.remove("user");
//刪除"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]);
}
- 支持classList 屬性的瀏覽器有Firefox 3.6+和Chrome。
2、焦點管理
(1)document.activeElement
- HTML5 也添加了輔助管理DOM 焦點的功能。首先就是document.activeElement 屬性,這個屬性始終會引用DOM 中當前獲得了焦點的元素。
- 元素獲得焦點的方式有頁面加載、用戶輸入(通常是通過按Tab鍵)和在代碼中調用focus()方法。
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
- 默認情況下,文檔剛剛加載完成時,document.activeElement 中保存的是document.body 元素的引用。
- 文檔加載期間,document.activeElement 的值為null。
(2)document.hasFocus()
- 新增了document.hasFocus()方法,這個方法用于確定文檔是否獲得了焦點。
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true
3、HTMLDocument的變化
(1)readyState 屬性
Document 的readyState 屬性有兩個可能的值:
- loading,正在加載文檔;
- complete,已經加載完文檔。
if (document.readyState == "complete"){
//執行操作
}
(2)兼容模式
- document的compatMode的屬性,告訴開發人員瀏覽器采用了哪種渲染模式。
- 在標準模式下,document.compatMode的值等于"CSS1Compat",而在混雜模式下,document.compatMode 的值等于"BackCompat"。
if (document.compatMode == "CSS1Compat"){
alert("Standards mode");
} else {
alert("Quirks mode");
}
(3)head屬性
-作為對document.body 引用文檔的<body>元素的補充,HTML5 新增了document.head 屬性,引用文檔的<head>元素。
var head = document.head || document.getElementsByTagName("head")[0];
4、字符集屬性
(1)charset
- charset 屬性表示文檔中實際使用的字符集,也可以用來指定新字符集。
- 默認情況下,這個屬性的值為"UTF-16",但可以通過<meta>元素、響應頭部或直接設置charset 屬性修改這個值。
alert(document.charset); //"UTF-16"
document.charset = "UTF-8";
(2)defaultCharset
- defaultCharset,表示根據默認瀏覽器及操作系統的設置,當前文檔默認的字符集
應該是什么。 - 如果文檔沒有使用默認的字符集,那charset 和defaultCharset 屬性的值可能會不一
樣。
if (document.charset != document.defaultCharset){
alert("Custom character set being used.");
}
5、自定義數據屬性
- HTML5 規定可以為元素添加非標準的屬性,但要添加前綴data-,目的是為元素提供與渲染無關的信息,或者提供語義信息。
- 這些屬性可以任意添加、隨便命名,只要以data-開頭即可。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
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);
}
6、插入標記
(1)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 = "_<script defer>alert('hi');<\/script>";
div.innerHTML = "<div> </div><script defer>alert('hi');<\/script>";
div.innerHTML = "<input type=\"hidden\"><script defer>alert('hi');<\/script>";
(2)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>本身。
- 在寫模式下,outerHTML會根據指定的HTML字符串創建新的DOM子樹,然后用這個DOM子樹完全替換調用元素。
- 使用outerHTML 屬性以下面這種方式設置值:
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);
(3)insertAdjacentHTML()方法
insertAdjacentHTML()方法接收兩個參數:插入位置和要插入的HTML 文本。
-
第一個參數必須是下列值之一,注意,這些值都必須是小寫形式:
- "beforebegin",在當前元素之前插入一個緊鄰的同輩元素;
- "afterbegin",在當前元素之下插入一個新的子元素或在第一個子元素之前再插入新的子元素;
- "beforeend",在當前元素之下插入一個新的子元素或在最后一個子元素之后再插入新的子元素;
- "afterend",在當前元素之后插入一個緊鄰的同輩元素。
第二個參數是一個HTML 字符串,如果瀏覽器無法解析該字符串,就會拋出錯誤。
//作為前一個同輩元素插入
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>");
(4)內存與性能問題
- 不可避免地,創建和銷毀HTML 解析器也會帶來性能損失,所以最好能夠將設置innerHTML
或outerHTML 的次數控制在合理的范圍內。
for (var i=0, len=values.length; i < len; i++){
ul.innerHTML += "<li>" + values[i] + "</li>"; //要避免這種頻繁操作!!
}
/**
最好的做法是單獨構建字符串,然后再一次
性地將結果字符串賦值給innerHTML
**/
var itemsHtml = "";
for (var i=0, len=values.length; i < len; i++){
itemsHtml += "<li>" + values[i] + "</li>";
}
ul.innerHTML = itemsHtml;
7、scrollIntoView()方法
- scrollIntoView()可以在所有HTML元素上調用,通過滾動瀏覽器窗口或某個容器元素,調用元素就可以出現在視口中。
- 如果給這個方法傳入true作為參數,或者不傳入任何參數,那么窗口滾動之后會讓調用元素的頂部與視口頂部盡可能平齊。
- 如果傳入false 作為參數,調用元素會盡可能全部出現在視口中,(可能的話,調用元素的底部會與視口頂部平齊。)不過頂部不一定平齊。
//讓元素可見
document.forms[0].scrollIntoView();
專有擴展
(1)文檔模式
- 要強制瀏覽器以某種模式渲染頁面,可以使用HTTP頭部信息X-UA-Compatible,或通過等價的<meta>標簽來設置:
<meta http-equiv="X-UA-Compatible" content="IE=IEVersion">
(2)children屬性
- 這個屬性是HTMLCollection 的實例,只包含元素中同樣還是元素的子節點。
var childCount = element.children.length;
var firstChild = element.children[0];
(3)contains()方法
- contains()方法接收一個參數,即要檢測的后代節點。如果被檢測的節點是后代節點,
該方法返回true;否則,返回false。
alert(document.documentElement.contains(document.body)); //true
- 使用DOM Level 3 compareDocumentPosition()也能夠確定節點間的關系。返回一個表示該關系的位掩碼( bitmask)。
- 支持這個方法的瀏覽器有IE9+、Firefox、Safari、Opera 9.5+和Chrome。
- 下表列出了這個位掩碼的值。
掩碼 | 節點關系 |
---|---|
1 | 無關(給定的節點不在當前文檔中) |
2 | 居前(給定的節點在DOM樹中位于參考節點之前) |
4 | 居后(給定的節點在DOM樹中位于參考節點之后) |
8 | 包含(給定的節點是參考節點的祖先) |
16 | 被包含(給定的節點是參考節點的后代) |
- 可以對compareDocumentPosition()的結果執行按位與,以確定參考節點(調用compareDocumentPosition()方法的當前節點)是否包含給定的節點(傳入的節點)。
var result = document.documentElement.compareDocumentPosition(document.body);
alert(!!(result & 16));
- 使用一些瀏覽器及能力檢測,就可以寫出如下所示的一個通用的contains 函數:
function contains(refNode, otherNode){
if (typeof refNode.contains == "function" &&
(!client.engine.webkit || client.engine.webkit >= 522)){
/**檢查了當前瀏覽器所用的WebKit 版本號,如果瀏覽器是WebKit 且至少是Safari 3(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;
}
}
(4)插入文本
- 在通過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 寫入值時,結果會刪除元素的所有子節點,插入包含相應文本值的文本節點。
div.innerText = "Hello world!";
- 執行這行代碼后,頁面的HTML 代碼就會變成如下所示。
<div id="content">Hello world!</div>
- Firefox 不支持innerText,支持作用類似的textContent 屬性。
- 為了確保跨瀏覽器兼容,有必要編寫一個類似于下面的函數來檢測可以使用哪個屬性。
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 與innerText 的結果完全一樣。
- 在寫模式下,outerText就完全不同了:outerText不只是替換調用它的元素的子節點,而是會替換整個元素(包括子節點)。
div.outerText = "Hello world!";
//這行代碼實際上相當于如下兩行代碼:
var text = document.createTextNode("Hello world!");
div.parentNode.replaceChild(text, div);
滾動
- scrollIntoViewIfNeeded(alignCenter):只在當前元素在視口中不可見的情況下,才滾
動瀏覽器窗口或容器元素,最終讓它可見。如果當前元素在視口中可見,這個方法什么也不做。如果將可選的alignCenter參數設置為true,則表示盡量將元素顯示在視口中部(垂直方向)。 - scrollByLines(lineCount):將元素的內容滾動指定的行高,lineCount 值可以是正值,
也可以是負值。 - scrollByPages(pageCount):將元素的內容滾動指定的頁面高度,具體高度由元素的高度決定。
The scrollByLines method is only supported by Firefox, use the cross-browser scrollBy method instead.