DOM(Document Object Model,文檔對(duì)象模型)是一個(gè)通過(guò)和 JavaScript 進(jìn)行內(nèi)容交互的 API。Javascript 和 DOM 一般經(jīng)常作為一個(gè)整體,因?yàn)?Javascript 通常都是用來(lái)進(jìn)行 DOM 操作和交互的。
http://wiki.jikexueyuan.com/project/javascript-depth-understanding/images/12.png" alt="" />
關(guān)于DOM,有些知識(shí)需要注意:
window 對(duì)象作為全局對(duì)象,也就是說(shuō)你可以通過(guò) window 來(lái)訪問(wèn)全局對(duì)象。
屬性在對(duì)象下面以變量的形式存放,在頁(yè)面上創(chuàng)建的所有全局對(duì)象都會(huì)變成 window 對(duì)象的屬性。
DOM 為 web 文檔創(chuàng)建帶有層級(jí)的結(jié)果,這些層級(jí)是通過(guò) node 節(jié)點(diǎn)組成,這里有幾種 DOM node 類型,最重要的是 Element,Text,Document。
<p>),你可以通過(guò)這個(gè) DOM 節(jié)點(diǎn)來(lái)訪問(wèn)。如果你正在使用 Firefox,我推薦你立即下載 Firebug 插件,對(duì)于你了解 DOM 結(jié)構(gòu)非常有用。
當(dāng)你在網(wǎng)站頁(yè)面上使用 JavaScript 的時(shí)候,需要使用 <script> 元素:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>JavaScript!</title>
</head>
<body>
<script type="text/javascript">
// <![CDATA[
// ]]>
</script>
</body>
</html>
上述代碼,嚴(yán)格來(lái)說(shuō) SCRIPT 的 TYPE 屬性應(yīng)該設(shè)置為 application/javascript,但是由于 IE 不支持這個(gè),所以平時(shí)我們不得不寫(xiě)成 text/javascript 或者直接去掉 type。另外你也可以看到在 SCRIPT 元素里的注釋行// <![CDATA[ 是用來(lái)告訴支持 XHTML 的瀏覽器,這里面的代碼是字符數(shù)據(jù)而不是 XHTML 標(biāo)簽,比如如果你在里面的數(shù)據(jù)使用了 < 或 >,瀏覽器就不會(huì)再解析成 XHTML 標(biāo)簽了。
任何在 SCRIPT 元素里聲明的代碼在頁(yè)面加載的時(shí)候都會(huì)運(yùn)行,唯一一個(gè)例外是給 SCRIPT 元素加上一個(gè) defer 屬性。defer 屬性告訴瀏覽器加載完HTML文檔以后再執(zhí)行 JS 代碼,但這個(gè)屬性只能在 IE 下使用。
如果你想了解外部腳本,只需要簡(jiǎn)單地在 SCRIPT 上使用 SRC 屬性就行了,使用單獨(dú)的 JS 文件的好處是可以緩存,而且也不需要擔(dān)心 CDATA 方面的問(wèn)題:
<script type="text/javascript" src="my-script.js"></script>
在我們繼續(xù) DOM 之前,我們來(lái)復(fù)習(xí)一下 JavaScript 的核心必備知識(shí),如果你還不了解,也沒(méi)關(guān)系,我們?cè)谶@一章節(jié)將稍微花點(diǎn)時(shí)間來(lái)回顧一下。
JavaScript 有幾種數(shù)據(jù)類型:Number,String,Boolean,Object,Undefined and Null。
單行注釋使用雙斜杠//,雙斜杠后面的所有文字都會(huì)被注釋掉,多行注意使用/*和*/括住。
在 JavaScript 里所有的 Number 都是浮點(diǎn)型的,當(dāng)聲明一個(gè)數(shù)字變量的時(shí)候,記得不要使用任何引號(hào)。
// 注:使用var類聲明變量
var leftSide = 100;
var topSide = 50;
var areaOfRectangle = leftSide * topSide; // = 5000
String
JavaScript 里聲明字符串特別簡(jiǎn)單,和其它語(yǔ)言一樣,在 JS 里使用單引號(hào)或雙引號(hào)都可以。
var firstPart = 'Hello';
var secondPart = 'World!';
var allOfIt = firstPart + ' ' + secondPart; // Hello World!
// +符合是字符連接符。也用于數(shù)字相加
布爾類型用于條件判斷,布爾類型是只有 2 個(gè)值:true 和 false。任何使用邏輯操作符的比較都會(huì)返回布爾值。
5 === (3 + 2); // = true
// 你也可以將布爾值賦給一個(gè)變量
var veryTired = true;
// 這樣使用
if (veryTired) {
// 執(zhí)行代碼
}
===也是比較操作符,不僅比較數(shù)值,還比較類型。
函數(shù)是特殊的對(duì)象。
// 使用function操作符來(lái)聲明新函數(shù)
function myFunctionName(arg1, arg2) {
// 函數(shù)代碼
}
// 你也可以聲明匿名函數(shù)
function (arg1, arg2) {
// Function code goes here.
}
// 運(yùn)行函數(shù)很簡(jiǎn)單,直接在函數(shù)名稱后面加上小括號(hào)就可以了
// 或者也可以帶上參數(shù)
myFunctionName(); // 無(wú)參
myFunctionName('foo', 'bar'); // 有參數(shù)
// 也可以使用自調(diào)用
(function () {
// 這里自調(diào)用函數(shù)
})();
數(shù)組也是特殊的對(duì)象,它包含了一批值(或?qū)ο螅L問(wèn)這些數(shù)據(jù)的話需要使用數(shù)字索引:
// 2種方式聲明數(shù)組
// 字面量:
var fruit = ['apple', 'lemon', 'banana'];
// Array構(gòu)造函數(shù):
var fruit = new Array('apple', 'lemon', 'banana');
fruit[0]; // 訪問(wèn)第1個(gè)項(xiàng)(apple)
fruit[1]; // 訪問(wèn)第2個(gè)項(xiàng)(lemon)
fruit[2]; // 訪問(wèn)第3個(gè)項(xiàng)(banana)
Object
一個(gè)對(duì)象是一個(gè) key-value 的集合,和數(shù)組相似,唯一的不同是你可以為每個(gè)數(shù)據(jù)定義一個(gè)名稱。
// 2種類型定義Object對(duì)象
// 字面量(大括號(hào))
var profile = {
name: 'Bob',
age: 99,
job: 'Freelance Hitman'
};
// 使用Object構(gòu)造函數(shù)
var profile = new Object();
profile.name = 'Bob';
profile.age = 99;
profile.job = 'Freelance Hitman';
JS 里使用最多的語(yǔ)句莫過(guò)于條件語(yǔ)句了:
var legalDrinkingAge = 21;
var yourAge = 29;
if ( yourAge >= legalDrinkingAge ) {
alert('You can drink.');
} else {
alert('Sorry, you cannot drink.');
建議你訪問(wèn)這個(gè)頁(yè)面來(lái)查看所有的 JS 操作符,這里我僅僅給出一些例子:
// 加減乘除
var someMaths = 2 + 3 + 4 - 10 * 100 / 2;
// 等于
if ( 2 == (5 - 3 ) { /* 代碼 */ } // == 比較是否相等
// 不等于
if ( 2 != (5 - 3 ) { /* 代碼 */ }
// 嚴(yán)格等于(推薦)
2 === 2 // 代替 2 == 2
2 !== 3 // 代替 2 != 3
// 賦值:
var numberOfFruit = 9;
numberOfFruit -= 2; // 等價(jià)于 "numberOfFruit = numberOfFruit - 2"
numberOfFruit += 2; // 等價(jià)于 "numberOfFruit = numberOfFruit + 2"
Loop 循環(huán)在是遍歷數(shù)組或者對(duì)象的所有成員的時(shí)候非常方便,JavaScript 里使用最多的是 FOR 和 WHILE 語(yǔ)句。
var envatoTutSites = ['NETTUTS', 'PSDTUTS', 'AUDIOTUTS', 'AETUTS', 'VECTORTUTS'];
// WHILE循環(huán)
var counter = 0;
var lengthOfArray = envatoTutSites.length;
while (counter < lengthOfArray) {
alert(envatoTutSites[counter]);
counter++; // 等價(jià)于counter += 1;
}
// FOR循環(huán)
// i只是用于迭代,可以任意取名
for (var i = 0, length = envatoTutSites.length; i < length; i++) {
alert(envatoTutSites[i]);
}
我們來(lái)個(gè)例子,一個(gè) HTML 里包含一段文本和一個(gè)無(wú)序的列表。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>JavaScript!</title>
</head>
<body>
<p id="intro">My first paragraph...</p>
<ul>
<li>List item 1</li>
<li>List item 1</li>
<li>List item 1</li>
<li>List item 1</li>
<li>List item 1</li>
</ul>
<script type="text/javascript">
// <![CDATA[
// ]]>
</script>
</body>
</html>
上面例子里,我們使用 getElementById DOM 方法來(lái)訪問(wèn) p 段落,在 SCRIPT 里添加如下代碼:
var introParagraph = document.getElementById('intro');
// 現(xiàn)在有了該DOM節(jié)點(diǎn),這個(gè)DOM節(jié)點(diǎn)展示的是該信息段落
變量 introParagraph 現(xiàn)在已經(jīng)引用到該 DOM 節(jié)點(diǎn)上了,我們可以對(duì)該節(jié)點(diǎn)做很多事情,比如查詢內(nèi)容和屬性,或者其它任何操作,甚至可以刪除它,克隆它,或者將它移到到 DOM 樹(shù)的其它節(jié)點(diǎn)上。
文檔上的任何內(nèi)容,我們都可以使用 JavaScript 和 DOM API 來(lái)訪問(wèn),所以類似地,我們也可以訪問(wèn)上面的無(wú)序列表,唯一的問(wèn)題是該元素沒(méi)有 ID 屬性,如果 ID 的話就可以使用相同的方式,或者使用如下 getElementsByTagName 方式:
var allUnorderedLists = document.getElementsByTagName('ul');
// 'getElementsByTagName'返回的是一個(gè)節(jié)點(diǎn)集合
// - 和數(shù)組有點(diǎn)相似
getElementsByTagName 方法返回的是一個(gè)節(jié)點(diǎn)集合,和數(shù)組類似也有 length 屬性,重要的一個(gè)特性是他是 live 的——如果你在該元素里添加一個(gè)新的 li 元素,這個(gè)集合就會(huì)自動(dòng)更新,介于他和數(shù)組類型,所以可以和訪問(wèn)數(shù)組一樣的方法來(lái)訪問(wèn),所以從 0 開(kāi)始:
// 訪問(wèn)無(wú)序列表: [0]索引
var unorderedList = document.getElementsByTagName('ul')[0];
// 獲取所有的li集合:
var allListItems = unorderedList.getElementsByTagName('li');
// 循環(huán)遍歷
for (var i = 0, length = allListItems.length; i < length; i++) {
// 彈出該節(jié)點(diǎn)的text內(nèi)容
alert(allListItems[i].firstChild.data);
}
以下圖例更清晰地展示了 DOM 獲取的知識(shí):
http://wiki.jikexueyuan.com/project/javascript-depth-understanding/images/13.png" alt="" />
“穿梭”這個(gè)詞主要是用來(lái)描述通過(guò) DOM 查找節(jié)點(diǎn),DOM API 提供了大量的節(jié)點(diǎn)屬性讓我們來(lái)往上或者往下查詢節(jié)點(diǎn)。
所有的節(jié)點(diǎn)都有這些屬性,都是可以用于訪問(wèn)相關(guān)的 node 節(jié)點(diǎn):
http://wiki.jikexueyuan.com/project/javascript-depth-understanding/images/14.png" alt="" />
通過(guò)這張圖,理解起來(lái)就簡(jiǎn)單多了,但有個(gè)非常重要的知識(shí)點(diǎn):那就是元素之間不能有空格,如果 ul 和 li 之間有空格的話,就會(huì)被認(rèn)為是內(nèi)容為空的 text node 節(jié)點(diǎn),這樣 ul.childNodes[0]就不是第一個(gè) li 元素了。相應(yīng)地,<p>的下一個(gè)節(jié)點(diǎn)也不是<ul>,因?yàn)?lt;p>和<ul>之間有一個(gè)空行的節(jié)點(diǎn),一般遇到這種情況需要遍歷所有的子節(jié)點(diǎn)然后判斷 nodeType 類型,1 是元素,2 是屬性,3 是 text 節(jié)點(diǎn),詳細(xì)的 type 類型可以通過(guò)此地址:
Node.ELEMENT_NODE == 1
Node.ATTRIBUTE_NODE == 2
Node.TEXT_NODE == 3
Node.CDATA_SECTION_NODE == 4
Node.ENTITY_REFERENCE_NODE == 5
Node.ENTITY_NODE == 6
Node.PROCESSING_INSTRUCTION_NODE == 7
Node.COMMENT_NODE == 8
Node.DOCUMENT_NODE == 9
Node.DOCUMENT_TYPE_NODE == 10
Node.DOCUMENT_FRAGMENT_NODE == 11
Node.NOTATION_NODE == 12
原生的 DOM 方法和屬性足夠我們?nèi)粘5膽?yīng)用了,本章節(jié)我們只列舉了一些例子,下一章節(jié)我們列舉更多的例子,還會(huì)包括瀏覽器事件模型。