JavaScript DOM編程藝術(shù)

前言

歸根結(jié)底,代碼都是思想和概念的體現(xiàn)搏熄。沒(méi)人能把一種程序設(shè)計(jì)語(yǔ)言的所有語(yǔ)法和關(guān)鍵字都記住,可以查閱參考書來(lái)解決暇赤。平穩(wěn)退化心例、漸進(jìn)增強(qiáng)、以用戶為中心的設(shè)計(jì)對(duì)任何前端Web開(kāi)發(fā)工作都非常重要鞋囊。

第1章 JavaScript簡(jiǎn)史

1 JavaScript的起源

Netscape公司和Sun公司合作開(kāi)發(fā)止后。第一個(gè)版本出現(xiàn)在1995年推出的Netscape Navigator2瀏覽器中。IE也以JScript為名發(fā)布了一個(gè)版本溜腐,面對(duì)IE的競(jìng)爭(zhēng)译株,Netscape和Sun聯(lián)合ECMA對(duì)JavaScript進(jìn)行了標(biāo)準(zhǔn)化——ECMAScript語(yǔ)言,就是現(xiàn)在談?wù)摰腏avaScript(以下均簡(jiǎn)稱JS)挺益。與Sun公司開(kāi)發(fā)的Java程序語(yǔ)言沒(méi)有任何關(guān)系歉糜。

2 DOM

DOM是一套對(duì)文檔內(nèi)容進(jìn)行抽象和概念化的方法。JS的早期版本向程序員提供了查詢和操控web文檔某些實(shí)際內(nèi)容的手段望众,主要是圖像和表單匪补,因?yàn)镴S預(yù)先定義了images和forms等術(shù)語(yǔ),通常把這種試驗(yàn)性質(zhì)的初級(jí)DOM成為0級(jí)DOM烂翰。

3 瀏覽器戰(zhàn)爭(zhēng)

Netscape Navigator 4發(fā)布于1997年6月夯缺,IE4發(fā)布于同年10月,兩者都大幅擴(kuò)展了DOM(但彼此不兼容)甘耿,使JS功能大大增加踊兜,出現(xiàn)一個(gè)新名詞:DHTML,即動(dòng)態(tài)HTML棵里,是描述HTML润文、CSS姐呐、JS技術(shù)組合的術(shù)語(yǔ)。

4 制定標(biāo)準(zhǔn)

瀏覽器制造商們攜手W3C于1998年制定完成了新的標(biāo)準(zhǔn)典蝌,即第1級(jí)DOM曙砂。標(biāo)準(zhǔn)化的DOM有遠(yuǎn)大的抱負(fù)载绿。

  • 瀏覽器以外的考慮眷蜈。DOM是一種API(應(yīng)用編程接口)噪舀,API是一組已經(jīng)得到有關(guān)各方共同認(rèn)可的基本約定颂龙,如國(guó)際時(shí)區(qū)苛萎、化學(xué)元素周期表等亏狰。W3C對(duì)DOM的定義是:一個(gè)與系統(tǒng)平臺(tái)和編程語(yǔ)言無(wú)關(guān)的接口放刨,程序和腳本可以通過(guò)這個(gè)接口動(dòng)態(tài)地訪問(wèn)和修改文檔的內(nèi)容贡羔、結(jié)構(gòu)和樣式葵袭。
  • 瀏覽器戰(zhàn)爭(zhēng)的結(jié)局涵妥。市場(chǎng)份額大戰(zhàn)中,微軟戰(zhàn)勝了Netscape坡锡。下一代瀏覽器產(chǎn)品對(duì)Web標(biāo)準(zhǔn)的支持得到了極大的改善蓬网。
  • 嶄新的起點(diǎn)。如今鹉勒,safari(WebKit)帆锋、Chrome(WebKit)、Firefox(Gecko)禽额、IE(Trident)和一些智能手機(jī)都對(duì)DOM有良好的支持锯厢。

第2章 JS語(yǔ)法

1 準(zhǔn)備工作

編寫JS腳本:文本編輯器+腳本,必須通過(guò)HTML/XHTML文檔執(zhí)行脯倒。
兩種方式:

  • 將JS代碼放到文檔<head>標(biāo)簽中的<script>標(biāo)簽之間
  • 把JS代碼存為一個(gè)擴(kuò)展名為.js的獨(dú)立文件实辑,在<head>部分放入<script>標(biāo)簽,將其src屬性指向該文件
  • 最好的做法是把<script>標(biāo)簽放在HTML文檔的最后盔憨,</body>之前徙菠,這樣能使瀏覽器更快地加載頁(yè)面

程序設(shè)計(jì)語(yǔ)言分為解釋型和編譯型兩大類。Java或C++等語(yǔ)言需要一個(gè)編譯器郁岩,能把源代碼翻譯為直接在計(jì)算機(jī)上執(zhí)行的文件婿奔。解釋型語(yǔ)言不需要編譯器,僅需要解釋器问慎。對(duì)于JS解釋器萍摊,瀏覽器負(fù)責(zé)完成有關(guān)的解釋和執(zhí)行工作。

2 語(yǔ)法

(1)語(yǔ)句如叼。建議在每條語(yǔ)句后面加分號(hào)冰木,這是一種良好的編程習(xí)慣。

(2)注釋。有效幫助了解代碼流程踊沸,多種注釋方法歇终。建議用“//”來(lái)注釋單行,用“/*”注釋多行逼龟。

(3)變量评凝。將值存入變量的操作,叫賦值腺律。提前聲明變量是一種良好的編程習(xí)慣奕短,雖然JS沒(méi)有強(qiáng)制要求。最有效率的方法是聲明和賦值一次完成:

var mood="happy",age=26;
相當(dāng)于:
var mood,age;
mood="happy";
age=33;

JS中變量和其他語(yǔ)法元素的名字都區(qū)分字母大小寫匀钧,不允許變量名中包括空格或標(biāo)點(diǎn)符號(hào)(美元符號(hào)除外)翎碑,允許包括字母、數(shù)字之斯、美元符號(hào)和下劃線日杈,第一個(gè)字符不允許是數(shù)字。如變量名my mood無(wú)效佑刷,可用my_mood达椰,或myMood,這種駝峰格式是函數(shù)名项乒、方法名和對(duì)象屬性名命名的首選格式。

(4)數(shù)據(jù)類型梁沧。上述代碼中檀何,變量mood的值是字符串,變量age的值是數(shù)字廷支,類型不同但聲明和賦值語(yǔ)法完全相同频鉴。有些其他語(yǔ)言還要求聲明數(shù)據(jù)類型,稱為強(qiáng)類型語(yǔ)言恋拍,而JS屬于弱類型語(yǔ)言垛孔。JS中的幾種數(shù)據(jù)類型:

  • 字符串。包括(但不限于)字母施敢、數(shù)字周荐、標(biāo)點(diǎn)符號(hào)和空格,必須包在引號(hào)里僵娃,單雙均可概作,但最好在整個(gè)文本里保持一致。如果字符串里本身含引號(hào)造成一定干擾默怨,可使用反斜線\進(jìn)行轉(zhuǎn)義讯榕,如‘don't ask'。
  • 數(shù)值。允許任意位小數(shù)愚屁,稱為浮點(diǎn)數(shù)济竹,也可以是負(fù)數(shù)。
  • 布爾值霎槐。布爾數(shù)據(jù)只有兩個(gè)可選值:true 或者false送浊。布爾值不是字符串,不要用引號(hào)括起來(lái)栽燕,false和“false”是兩碼事罕袋。

(5)數(shù)組。字符串碍岔、數(shù)值和布爾值都是標(biāo)量(scalar)浴讯,它只能有一個(gè)值。而數(shù)組是指用一個(gè)變量表示一個(gè)值的集合蔼啦,集合中的每個(gè)值都是這個(gè)數(shù)組的一個(gè)元素榆纽。
JS數(shù)組可以用關(guān)鍵字Array聲明,聲明的同時(shí)還可以指定初始元素個(gè)數(shù)捏肢,即數(shù)組長(zhǎng)度奈籽。

var beatles=Array(4);或 var beatles=Array()

向數(shù)組添加元素的操作稱為填充(populating)。填充時(shí)鸵赫,需要給出新元素的值及其在數(shù)組中的存放位置衣屏,即元素的下標(biāo)(index),下標(biāo)必須用方括號(hào)括起來(lái):

array[index]=element;
如,var beatles=Array(4);
    beatles[0]="John";(JS規(guī)定0作為第一個(gè)下標(biāo))
    beatles[1]="Paul";
    beatles[2]="George";
    beatles[3]="Ringo";
簡(jiǎn)便寫法:
var beatles=Array("John","Paul","George","Ringo");
或者:
var beatles=["John","Paul","George","Ringo"];

數(shù)組元素可以是字符辩棒、數(shù)字狼忱、布爾值:

var lennon=["John",1940,false];

還可以是變量:

var name="John";
var beatles[0]=name;

還可以是另一個(gè)數(shù)組的元素:

var names=["Ringo","John","George","Paul"];
beatles[1]=names[3];

還可以包含其他的數(shù)組:

var lennon=["John",1940,false];
var beatles=[];
beatles[0]lennon;
則beatles[0][1]的值是1940.

但如果要記住每個(gè)下標(biāo)數(shù)字的話,編程工作將十分麻煩一睁,幸好還有其他方法可以填充數(shù)組:關(guān)聯(lián)數(shù)組钻弄;將數(shù)組保存為對(duì)象。
關(guān)聯(lián)數(shù)組:
如果在填充數(shù)組時(shí)只給出了元素的值者吁,這個(gè)數(shù)組將是一個(gè)傳統(tǒng)數(shù)組窘俺,它的各個(gè)元素的下標(biāo)將被自動(dòng)創(chuàng)建和刷新。在為新元素給出下標(biāo)時(shí)复凳,不必局限于使用整數(shù)數(shù)字瘤泪,還可以用字符串:

var lennon=Array();
lennon["name"]="John";
lennon["year"]=1940;
lennon["living"]=false;

這樣的數(shù)組叫關(guān)聯(lián)數(shù)組,代碼更具有可讀性育八,但這種用法不是一個(gè)好習(xí)慣均芽,不推薦使用。本質(zhì)上单鹿,在創(chuàng)建關(guān)聯(lián)數(shù)組時(shí)掀宋,創(chuàng)建的是Array對(duì)象的屬性。在JS中所有變量實(shí)際上都是某種類型的對(duì)象。理想情況下劲妙,不應(yīng)該修改Array對(duì)象的屬性湃鹊,而應(yīng)該使用通用的對(duì)象(Object)。
(6)對(duì)象
與數(shù)組類似镣奋,對(duì)象也是使用一個(gè)名字表示一組值币呵。對(duì)象的每個(gè)值都是對(duì)象的一個(gè)屬性,前一節(jié)的lennon數(shù)組也可以創(chuàng)建成下面這個(gè)對(duì)象:

var lennon=Object();
lennon.name="John";
lennon.year=1940;
lennon.living=false;

它不使用方括號(hào)和下標(biāo)來(lái)獲取元素侨颈,而是像任何JS對(duì)象一樣余赢,使用點(diǎn)號(hào)來(lái)獲取屬性。創(chuàng)建對(duì)象還有一種更簡(jiǎn)潔的語(yǔ)法哈垢,即花括號(hào)語(yǔ)法:

var lennon={name:"John",year:1940,living:false};

用對(duì)象來(lái)代替?zhèn)鹘y(tǒng)數(shù)組的做法意味著可以通過(guò)元素的名字而不是下標(biāo)數(shù)字來(lái)引用它們妻柒,這大大提高了腳本的可讀性。

3 操作

進(jìn)行計(jì)算和處理數(shù)據(jù)耘分,即操作(operation)举塔。
算術(shù)操作符
變量可以包含操作:var total=(1+4)*5;

還可以對(duì)變量進(jìn)行操作:var temp_fahrenheit=95;var temp_celsius=(temp_fahrenheit-32)/1.8;
加號(hào)(+)是一個(gè)比較特殊的操作符,既可用于數(shù)值求泰,也可用于字符串:

var message="I am feeling"+"happy";

這種把多個(gè)字符串首尾相連的操作叫拼接(concatenation)央渣。
拼接可通過(guò)變量完成:

var mood="happy";
var message="I am feeling"+mood;
還可以把數(shù)值和字符串拼接
var year=2005;
var message="The year is"+year;

把字符串和數(shù)字拼接在一起,結(jié)果是一個(gè)更長(zhǎng)的字符串渴频,兩個(gè)數(shù)值拼接在一起芽丹,結(jié)果是兩個(gè)數(shù)值的算術(shù)和。
快捷操作符:
++相當(dāng)于+1卜朗;
+=則是一次完成“拼接和賦值”:

var year=2010;
var message="The year is";
message+=year;
結(jié)果是變量message的值為“The year is 2005”志衍。

4 條件語(yǔ)句

最常見(jiàn)的條件語(yǔ)句是if語(yǔ)句,基本語(yǔ)法:if (condition) {statements;},其中condition的求值結(jié)果永遠(yuǎn)是一個(gè)布爾值聊替。if語(yǔ)句可以有一個(gè)else子句,其語(yǔ)句會(huì)在給定condition為false時(shí)執(zhí)行培廓。
(1)比較操作符惹悄。

  • 等于。注意(=)是賦值,(==)才是等于肩钠。(==)不表示嚴(yán)格相等泣港,而(===)表示全等操作符,不僅比較值价匠,還會(huì)比較變量類型当纱。
var my_mood="happy";
var your_mood="sad";
if (my_mood==your_mood){
alert("we both feel the same");
}
  • 不等于。(!=)踩窖,嚴(yán)格不相等需使用(!==)
if (my_mood != your_mood){
alert("We are feeling different moods.");
}

(2)邏輯操作符坡氯。把條件語(yǔ)句里的操作組合在一起,需使用邏輯操作符。

  • 邏輯與&&箫柳。兩個(gè)操作數(shù)都true才是true手形。
if (num>=5 && num<=10){
alert("The number is in the right range.");
}
  • 邏輯或||。一個(gè)true就是true悯恍。
  • 邏輯非库糠!。
if (!(5>10||5<1)){
alert("The number is in the right range.");
}

5 循環(huán)語(yǔ)句

if 語(yǔ)句的不足是無(wú)法完成重復(fù)性操作涮毫,如需多次執(zhí)行同一個(gè)代碼塊瞬欧,必須使用循環(huán)語(yǔ)句,其工作原理是只要給定條件仍能滿足罢防,包含在循環(huán)語(yǔ)句里的代碼就會(huì)重復(fù)執(zhí)行艘虎,一旦給定條件的求值結(jié)果不再是true,循環(huán)終止。
(1)while 循環(huán)篙梢。與if語(yǔ)句的語(yǔ)法幾乎完全一樣顷帖,唯一的區(qū)別是只要給定條件求值結(jié)果是true,花括號(hào)里的代碼就將反復(fù)執(zhí)行。

var count=1;
while(count<11){
alert(count);
count++;
}

對(duì)循環(huán)控制條件的求值發(fā)生在每次循環(huán)開(kāi)始之前渤滞,如果控制條件首次求值結(jié)果是false贬墩,那么花括號(hào)里的代碼將一個(gè)也不會(huì)被執(zhí)行。在某些場(chǎng)合妄呕,為保證代碼至少執(zhí)行一次陶舞,用到do...while循環(huán),其對(duì)循環(huán)控制條件的求值發(fā)生在每次循環(huán)結(jié)束之后绪励。

var count=1;
do{
alert(count);
count++;
}while(count<11);

(2)for循環(huán)肿孵。是while循環(huán)的一種變體。
for(initial condition;test condition;alter condition){
statements;
} 用for循環(huán)來(lái)重復(fù)執(zhí)行代碼的好處是循環(huán)控制結(jié)構(gòu)更加清晰疏魏。上例:

for (var count=1; count<11;count++){
alert(count);
}

for循環(huán)最常見(jiàn)的用途之一是對(duì)某個(gè)數(shù)組里的全體元素進(jìn)行遍歷處理:

var beatles=Array("John","Paul","George","Ringo");
for(var count=0;count<beatles.length;count++){
alert(beatles[count]);
}

6 函數(shù)

每當(dāng)需要反復(fù)做一件事時(shí)停做,都可以利用函數(shù)來(lái)避免重復(fù)鍵入大量相同內(nèi)容,不過(guò)函數(shù)的真正威力體現(xiàn)在大莫,你可以把不同數(shù)據(jù)傳遞給它們蛉腌,完成預(yù)定操作。把傳遞給函數(shù)的數(shù)據(jù)稱為參數(shù)(argument)只厘。JS提供了很多內(nèi)建函數(shù)烙丛,如前面的alert。
定義一個(gè)函數(shù)的語(yǔ)法:

function name(arguments){
statements;
}
如傳遞兩個(gè)參數(shù)的函數(shù):
function multiply(num1,num2){
var total=num1*num2;
alert(total);
}
定義了這個(gè)函數(shù)的腳本羔味,可從任意位置調(diào)用
multiply(10,2);

函數(shù)不僅能夠以參數(shù)的形式接收數(shù)據(jù)河咽,還能夠返回?cái)?shù)據(jù),需用到return語(yǔ)句赋元。
函數(shù)的真正價(jià)值體現(xiàn)在忘蟹,可以把它們當(dāng)做一種數(shù)據(jù)類型來(lái)使用飒房,變量用下劃線,函數(shù)用駝峰命名法寒瓦,可予以區(qū)分情屹。
變量的作用域(scope)

  • 全局變量global variable,可以在腳本中的任何位置被引用杂腰。
  • 局部變量local variable垃你,只存在于聲明它的函數(shù)內(nèi)部。
    如果在某個(gè)函數(shù)中使用了var喂很,那個(gè)變量就是局部變量惜颇,否則視為全局變量,如果腳本里已經(jīng)存在一個(gè)與之同名的全局變量少辣,這個(gè)函數(shù)就會(huì)改變那個(gè)全局變量的值凌摄。所以定義一個(gè)函數(shù)時(shí),一定要把它內(nèi)部的變量全都明確地聲明為局部變量漓帅,避免隱患锨亏。

7 對(duì)象

包含在對(duì)象里的數(shù)據(jù)可以通過(guò)兩種形式訪問(wèn)——屬性property和方法method。屬性是隸屬于某個(gè)特定對(duì)象的變量忙干,方法是只有某個(gè)特定對(duì)象才能調(diào)用的函數(shù)器予。

Person.mood
Person.age
Person.walk()
使用new關(guān)鍵字,為對(duì)象創(chuàng)建實(shí)例instance:
var jeremy=new Person捐迫;
就可以用Person對(duì)象的屬性來(lái)檢索關(guān)于jeremy的信息了:
jeremy.age
jeremy.mood

(1)內(nèi)建對(duì)象乾翔。
數(shù)組就是一種,還有Meth施戴、Date等反浓。
(2)宿主對(duì)象。
這些對(duì)象不是由JS語(yǔ)言本身而是由它的運(yùn)行環(huán)境提供的赞哗。具體到Web應(yīng)用雷则,這個(gè)環(huán)境就是瀏覽器,由瀏覽器提供的預(yù)定義對(duì)象被稱為宿主對(duì)象肪笋,如Form月劈、Image等。

第3章 DOM

1 文檔:DOM中的D

當(dāng)創(chuàng)建了一個(gè)網(wǎng)頁(yè)并把它加載到Web瀏覽器中時(shí)涂乌,你編寫的網(wǎng)頁(yè)文檔就轉(zhuǎn)換為一個(gè)文檔對(duì)象了。

2 對(duì)象:DOM中的O

JS對(duì)象分三種類型:用戶自定義對(duì)象英岭、內(nèi)建對(duì)象湾盒、宿主對(duì)象。

3 模型:DOM中的M

就像一個(gè)模型火車代表著一列真正的火車诅妹,DOM代表著加載到瀏覽器窗口的當(dāng)前網(wǎng)頁(yè)罚勾。DOM把文檔表示為一棵家譜樹(shù)毅人,稱為“節(jié)點(diǎn)樹(shù)”更準(zhǔn)確。

4 節(jié)點(diǎn)

節(jié)點(diǎn)node表示網(wǎng)絡(luò)中的一個(gè)連接點(diǎn)尖殃,一個(gè)網(wǎng)絡(luò)就是由一些節(jié)點(diǎn)構(gòu)成的集合丈莺。
DOM里有三種不同類型的節(jié)點(diǎn):元素節(jié)點(diǎn)、文本節(jié)點(diǎn)送丰、屬性節(jié)點(diǎn)。
獲取元素

  • getElementById.這個(gè)方法將返回一個(gè)與有著給定id屬性值的元素節(jié)點(diǎn)對(duì)應(yīng)的對(duì)象。如document.getElementById("purchases")驱入。
  • getElementsByTagName.這個(gè)方法將返回一個(gè)對(duì)象數(shù)組多矮,每個(gè)對(duì)象分別對(duì)應(yīng)著文檔里有著給定標(biāo)簽的一個(gè)元素。為了減少不必要的打字量并改善代碼可讀性登失,可把document.getElementsByTagName("")賦值給一個(gè)變量遏佣。
    getElementsByTagName還允許把通配符作為參數(shù),如alert(document.getElementsByTagName("*").length);
    還可以跟getElementById結(jié)合起來(lái)運(yùn)用揽浙。
  • getElementsByClassName.

5 獲取和設(shè)置屬性

(1)getAttribute.是只有一個(gè)參數(shù)的函數(shù)状婶,但它不屬于document對(duì)象,所以不能通過(guò)document對(duì)象調(diào)用馅巷,只能通過(guò)元素節(jié)點(diǎn)對(duì)象調(diào)用膛虫。
(2)setAttribute.它允許我們隊(duì)屬性節(jié)點(diǎn)值做出修改,也是只能用于元素節(jié)點(diǎn)令杈。
object.setAttribute(attribute,value)

var shopping = document.getElementById("purchases");
alert(shopping.getAttribute("title"));
shopping.setAttribute("title","a list of goods");
alert(shopping.getAttribute("title"));

第一個(gè)alert顯示null走敌,第二個(gè)alert顯示a list of goods。這表明setAttribute實(shí)際上完成了兩項(xiàng)操作:先創(chuàng)建屬性逗噩,然后設(shè)置它的值掉丽。
一個(gè)細(xì)節(jié):通過(guò)setAttribute對(duì)文檔做出修改后,查看源代碼時(shí)看到的仍是改變前的屬性值异雁。這種現(xiàn)象源自DOM的工作模式:先加載文檔的靜態(tài)內(nèi)容捶障,再動(dòng)態(tài)刷新,動(dòng)態(tài)刷新不影響文檔的靜態(tài)內(nèi)容纲刀。

第4章 案例研究:JS圖片庫(kù)

1 標(biāo)記

第一項(xiàng)工作是為這些圖片創(chuàng)建一個(gè)鏈接清單项炼。如果圖片有排序,用<ol>示绊,無(wú)排序锭部,用 <ul>。增加一個(gè)占位符圖片為圖片預(yù)留一個(gè)瀏覽區(qū)域面褐。

    <ul>
    <li>
    <a href="images/1.jpg" onclick="showPic(this);return false;" title="gray">gray</a>
    </li>
    <li>
    <a href="images/2.jpg" onclick="showPic(this);return false;" title="ziont1">ziont</a>
    </li>
    </ul>
    <img id="placeholder" src="" alt="my image gallery">

2 JS

function showPic(whichpic){
var source = whichpic.getAttribute("href");
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src",source);
}

函數(shù)取名為showPic,其參數(shù)取名為whichpic.通過(guò)getAttribute獲取whichpic對(duì)象的href屬性,通過(guò)getElementById獲取占位符圖片,通過(guò)setAttribute更改占位符圖片的src屬性拌禾。

3 應(yīng)用這個(gè)函數(shù)

需要在html文件中添加事件處理函數(shù)(event handler).
事件處理函數(shù)的作用是,在特定事件發(fā)生時(shí)調(diào)用特定的JS代碼展哭。如onmouseover湃窍、onmouseout闻蛀、onclick函數(shù)。
當(dāng)把onclick函數(shù)嵌入到一個(gè)鏈接中時(shí)您市,需要把這個(gè)鏈接本身用作showPic函數(shù)的參數(shù)觉痛,可使用this關(guān)鍵字:onclick="showPic(this);"
但是,點(diǎn)擊這個(gè)鏈接時(shí)茵休,不僅showPic函數(shù)被調(diào)用薪棒,鏈接被點(diǎn)擊的默認(rèn)行為也會(huì)被調(diào)用,如何阻止這個(gè)默認(rèn)行為被調(diào)用泽篮。事件處理函數(shù)的工作機(jī)制是盗尸,一旦事件發(fā)生,相應(yīng)的JS代碼就會(huì)被執(zhí)行帽撑。被調(diào)用的JS代碼可以返回給事件函數(shù)一個(gè)值泼各。如果讓返回的是一個(gè)布爾值,如該例中return false亏拉,則onclick就認(rèn)為“這個(gè)鏈接沒(méi)有被點(diǎn)擊”扣蜻。

4 擴(kuò)展這個(gè)函數(shù)

(1)childNodes屬性。
在一棵節(jié)點(diǎn)樹(shù)上及塘,childNodes屬性可以用來(lái)獲取任何一個(gè)元素的所有子元素莽使,它是一個(gè)包含這個(gè)元素全部子元素的數(shù)組:element.childNodes.
如需精確查出body元素一共有多少個(gè)子元素:

function countBodyChildren(){
var body_element = document.getElementsByTagName("body")[0];
alert(body_element.childNodes.length);
}
window.onload = countBodyChildren;

(2)nodeType屬性。
nodeType共有12種可取值笙僚,但其中僅有3種具有實(shí)用價(jià)值芳肌。

  • 元素節(jié)點(diǎn)的nodeType屬性值是1
  • 屬性節(jié)點(diǎn)的nodeType屬性值是2
  • 文本節(jié)點(diǎn)的nodeType屬性值是3

(3)為標(biāo)記里增加一段描述。
首先肋层,為目標(biāo)文本安排顯示位置亿笤,設(shè)置id值。

<p id="description">Choose an picture.</p>

目的是圖片鏈接被點(diǎn)擊時(shí)栋猖,不僅把占位符圖片替換為那個(gè)href屬性指向的圖片净薛,還要把這段文本同時(shí)替換為那個(gè)圖片鏈接的title屬性值。
(4)用JS改變這段描述蒲拉。
修改showPic()函數(shù):

function showPic(whichpic){
var source = whichpic.getAttribute("href");
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src",source);
var text = whichpic.getAttribute("title");
var description = document.getElementById("description");
}

(5)nodeValue屬性肃拜。
如果想改變一個(gè)文本節(jié)點(diǎn)的值,要使用DOM提供的nodeValue屬性雌团,它用來(lái)得到(和設(shè)置)一個(gè)節(jié)點(diǎn)的值燃领。注意:<p>元素本身的nodeValue屬性是一個(gè)空值,包含在<p>元素里的文本是另一種節(jié)點(diǎn)锦援,它是p元素的第一個(gè)子節(jié)點(diǎn)猛蔽。要修改p元素的文本值,需要獲取的是文本而不是p雨涛,因此下面兩條語(yǔ)句枢舶,第一條返回null,第二條才是文本值替久。

alert(description.nodeValue);
alert(description.childNodes[0].nodeValue);

(6)firstChild和lastChild屬性凉泄。
數(shù)組元素childNodes[0]有個(gè)更直觀易讀的同義詞:firstChild.與之對(duì)應(yīng)的是lastChild.
(7)利用nodeValue屬性刷新這段描述。

function showPic(whichpic){
var source = whichpic.getAttribute("href");
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src",source);
var text = whichpic.getAttribute("title");
var description = document.getElementById("description");
description.firstChild.nodeValue = text;
}

第5章 最佳實(shí)踐

1 過(guò)去的錯(cuò)誤

(1)JS
易學(xué)易用的技術(shù)是一把雙刃劍蚯根,容易被廣泛應(yīng)用后众,但往往缺乏高水平的質(zhì)量控制。一些現(xiàn)成的JS函數(shù)里有很多問(wèn)題考慮不周全颅拦。一旦瀏覽器不支持或禁用了JS解釋功能蒂誉,那些質(zhì)量低劣的腳本就會(huì)導(dǎo)致用戶無(wú)法瀏覽相應(yīng)的網(wǎng)頁(yè)甚至整個(gè)網(wǎng)站。
(2)flash
(3)質(zhì)疑
網(wǎng)站對(duì)JS的濫用已經(jīng)持續(xù)了相當(dāng)長(zhǎng)的時(shí)間距帅。如果要使用JS右锨,就要確認(rèn):這么做會(huì)對(duì)用戶瀏覽體驗(yàn)產(chǎn)生什么影響?用戶瀏覽器不支持JS該怎么辦碌秸?

2 平穩(wěn)退化

如果正確地使用了JS腳本绍移,就可以讓訪問(wèn)者在他們?yōu)g覽器不支持JS的情況下仍能順利地瀏覽你的網(wǎng)站,這就是所謂的平穩(wěn)退化讥电,就是說(shuō)雖然某些功能無(wú)法使用蹂窖,但最基本的操作仍能順利完成。舉例:創(chuàng)建新的瀏覽器窗口 window.open(url,name,features)
(1)JS偽協(xié)議
“真”協(xié)議用來(lái)在因特網(wǎng)上的計(jì)算機(jī)之間傳輸數(shù)據(jù)包恩敌,如HTTP協(xié)議瞬测、FTP協(xié)議等,偽協(xié)議則是一種非標(biāo)準(zhǔn)化的協(xié)議纠炮,偽協(xié)議讓我們通過(guò)一個(gè)鏈接來(lái)調(diào)用JS函數(shù)月趟。
如調(diào)用popUp()函數(shù):

<a hret="javascript:popUp('http://www.example.com/');">Example</a>

這條語(yǔ)句在支持“javascript:”偽協(xié)議的瀏覽器中運(yùn)行正常,較老的瀏覽器會(huì)失敗抗碰,支持這種偽協(xié)議但禁用了JS功能的瀏覽器什么也不會(huì)做狮斗。總之在HTML文檔中通過(guò)“javascript:”偽協(xié)議調(diào)用JS代碼的做法非常不好弧蝇。
(2)內(nèi)嵌的事件處理函數(shù)

<a href="#" onclick="popUp('http://www.example.com/');
return false;">Example</a>

把href值設(shè)置為“#”只是為了創(chuàng)建一個(gè)空鏈接碳褒,實(shí)際工作全部由onclick屬性負(fù)責(zé)完成。這個(gè)方法同樣不能平穩(wěn)退化看疗。
(3)平穩(wěn)退化的重要性
一個(gè)重要的訪問(wèn)者:搜索機(jī)器人(searchbot)沙峻。目前只有極少數(shù)搜索機(jī)器人能理解JS代碼(?)两芳。如果你的JS網(wǎng)頁(yè)不能平穩(wěn)退化摔寨,它們?cè)谒阉饕嫔系呐琶涂赡艽笫軗p害。一個(gè)解決辦法怖辆,具體到popUp()函數(shù)是复,把href屬性設(shè)置為真實(shí)存在的URL地址:

<a 
onclick="popUp('http://www.example.com');return false;">Example</a>
上述代碼可簡(jiǎn)化為
<a 
onclick="popUp(this.href);return false;">Example</a>

3 向CSS學(xué)習(xí)

(1)結(jié)構(gòu)與樣式的分離
具備CSS支持的瀏覽器可以把網(wǎng)頁(yè)呈現(xiàn)得美輪美奐删顶,不支持或禁用了CSS功能的瀏覽器同樣可以把網(wǎng)頁(yè)的內(nèi)容按照正確的結(jié)構(gòu)顯示出來(lái)。
(2)漸進(jìn)增強(qiáng)
所謂“漸進(jìn)增強(qiáng)”就是用一些額外的信息層去包裹原始數(shù)據(jù)淑廊,按照漸進(jìn)增強(qiáng)原則創(chuàng)建出來(lái)的網(wǎng)頁(yè)幾乎都符合平穩(wěn)退化原則逗余。如果說(shuō)CSS是提供“表示”,則JS是提供“行為”季惩。把CSS代碼從HTML文檔里分離出來(lái)可以讓CSS工作得更好录粱,這同樣適用于JS行為層。

4 分離JS

<a  class="popup">Example</a>

如何實(shí)現(xiàn)當(dāng)這個(gè)鏈接被點(diǎn)擊時(shí)画拾,它將調(diào)用popUp()函數(shù):JS語(yǔ)言不要求事件必須在HTML文檔里處理啥繁,可以在外部JS文件里把一個(gè)事件添加到HTML文檔中的某個(gè)元素上,可以利用class或id屬性來(lái)解決青抛。具體步驟:
(1)把文檔中所有鏈接全放入一個(gè)數(shù)組里
(2)遍歷數(shù)組
(3)如果某個(gè)鏈接的class屬性等于popup旗闽,就表示這個(gè)鏈接在被點(diǎn)擊時(shí)應(yīng)調(diào)用popUp()函數(shù)。

window.onload = prepareLinks;
function prepareLinks(){
var links = document.getElementsByTagName("a");
for (var i=0;i<links.length;i++){
if (links[i].getAttribute("class")=="popup"){
links[i].onclick=function(){
popUp(this.getAttribute("href"));
return false;
}
}
}
}
function popUp(winURL){
window.open(winURL,"popup",width=320,height=480");
}

以上代碼將調(diào)用popUp()函數(shù)的onclick事件添加到有關(guān)鏈接上蜜另,等于把這些操作從HTML文檔里分離出來(lái)宪睹,這就是“分離JavaScript”。另外蚕钦,為保證HTML文檔加載完再加載腳本亭病,可將代碼打包到preparelinks()函數(shù),并將其添加到windows對(duì)象的onload事件上嘶居。

5向后兼容

(1)對(duì)象檢測(cè)
檢測(cè)瀏覽器對(duì)JS的支持程度罪帖。只要把某個(gè)方法打包在一個(gè)if語(yǔ)句里,就可以根據(jù)這條if語(yǔ)句的條件表達(dá)式求值結(jié)果是true還是false來(lái)決定采取怎樣的行動(dòng)邮屁。這種檢測(cè)稱為對(duì)象檢測(cè)整袁。

if(method){
statements
}

但如此編寫出來(lái)的函數(shù)會(huì)增加一對(duì)花括號(hào),如果需要在函數(shù)里檢測(cè)多個(gè)DOM方法和/或?qū)傩允欠翊嬖谟恿撸@個(gè)函數(shù)最重要的語(yǔ)句就會(huì)深埋在一層又一層的花括號(hào)里坐昙,這樣的代碼往往很難閱讀和理解。把測(cè)試條件改為“如果你不理解這個(gè)方法芋忿,就離開(kāi)”則更簡(jiǎn)單炸客。如:

if(!getElementById || !getElementsByTagName)return false;

(2)瀏覽器嗅探技術(shù)
這是一種風(fēng)險(xiǎn)很大的技術(shù)。一是瀏覽器有時(shí)會(huì)撒謊戈钢,二是為適用于多種瀏覽器痹仙,嗅探腳本會(huì)越來(lái)越復(fù)雜,三是許多嗅探腳本在進(jìn)行此類測(cè)試時(shí)殉了,要求瀏覽器版本號(hào)必須得到精確匹配开仰,因此需要一直修改。這種技術(shù)正在被更簡(jiǎn)單更健壯的對(duì)象檢測(cè)技術(shù)所取代。

6 性能考慮

(1)盡量少訪問(wèn)DOM和盡量減少標(biāo)記众弓。在多個(gè)函數(shù)都會(huì)取得一組類似元素的情況下恩溅,可以考慮重構(gòu)代碼,把搜索結(jié)果保存在一個(gè)全局變量里谓娃,或者把一組元素直接以參數(shù)形式傳遞給函數(shù)暴匠。并且要盡量減少文檔中的標(biāo)記數(shù)量。
(2)合并和放置腳本傻粘。包含腳本的最佳方式就是使用外部文件。減少請(qǐng)求數(shù)量通常都是在性能優(yōu)化時(shí)首先要考慮的帮掉。腳本在標(biāo)記中的位置對(duì)頁(yè)面的初次加載時(shí)間也有很大影響弦悉。傳統(tǒng)上放在<head>里,但會(huì)導(dǎo)致瀏覽器無(wú)法并行加載其他文件蟆炊。一般來(lái)說(shuō)根據(jù)HTTP規(guī)范稽莉,瀏覽器每次從同一域名中最多只能同時(shí)下載兩個(gè)文件。把所有<script>標(biāo)簽都放到文檔的末尾涩搓,</body>標(biāo)記前污秆,可以讓頁(yè)面變得更快(?)
(3)壓縮腳本昧甘。是指把腳本文件中不必要的字節(jié)良拼,如空格和注釋統(tǒng)統(tǒng)刪除。有許多工具可以用來(lái)精簡(jiǎn)代碼充边。多數(shù)情況下你應(yīng)該有兩個(gè)版本庸推,一個(gè)是工作副本,可以修改代碼并添加注釋浇冰,另一個(gè)是精簡(jiǎn)副本贬媒,用于放在站點(diǎn)上,為了與非精簡(jiǎn)版本區(qū)分開(kāi)肘习,可在文件名中加上min字樣际乘。

第6章 案例研究:圖片庫(kù)改進(jìn)

1 圖片庫(kù)代碼回顧

2 平穩(wěn)退化

此代碼(見(jiàn)第5章)即使在JS功能被禁用,用戶也可以瀏覽圖片庫(kù)里的圖片漂佩,所有鏈接也都正常工作脖含。但如果選用“javascript:”偽協(xié)議,或把鏈接寫成#投蝉,禁用JS的用戶將無(wú)法瀏覽圖片器赞。

3 JS與HTML是分離的嗎

因?yàn)樵趆tml里插入了onclick事件,所以JS和HTML是混在在一起的墓拜。把JS移出HTML有多種方法港柜,如給每個(gè)鏈接添加class屬性。但注意圖片清單里各個(gè)鏈接有一個(gè)共同點(diǎn),他們都包含在同一個(gè)列表清單元素里夏醉,因此給列表清單設(shè)置一個(gè)id比較簡(jiǎn)單爽锥。
(1)添加事件處理函數(shù)。
想要完成的工作:

  • 檢查當(dāng)前瀏覽器是否理解getElementsByTagName
  • 檢查當(dāng)前瀏覽器是否理解getElementById
  • 檢查當(dāng)前網(wǎng)頁(yè)是否有id為imagegallery的元素
  • 遍歷imagegallery中的所有鏈接
  • 設(shè)置onclick事件畔柔,讓它在有關(guān)鏈接被點(diǎn)擊時(shí)完成以下操作:把這個(gè)鏈接作為參數(shù)傳給showPic()函數(shù)氯夷,取消鏈接被點(diǎn)擊時(shí)的默認(rèn)行為。
    ①檢查點(diǎn)
    if(!document.getElementsByTagName||!document.getElementById)return false;
    if(!document.getElementById(“imagegallery”))return false;
    出于JS與HTML分離的原則靶擦,如果想用JS給某個(gè)網(wǎng)頁(yè)添加一些行為腮考,就不應(yīng)該讓JS代碼對(duì)這個(gè)網(wǎng)頁(yè)的結(jié)構(gòu)有任何依賴。
    ②變量名里有什么
    創(chuàng)建一個(gè)“gallery”變量玄捕,選擇一些有意義的單詞來(lái)命名可以讓代碼更容易閱讀和理解踩蔚。但要注意有些單詞在JS語(yǔ)言中有特殊的含義和用途,這些統(tǒng)稱為“保留字”的單詞不能用作變量名枚粘,另外馅闽,現(xiàn)有JS函數(shù)或方法的名字,如alert馍迄、var福也、if,也不能用來(lái)命名變量。
    var galley=document.getElementById("imagegallery");
    var links=gallery.getElementsByTagName("a");
    ③遍歷
    把充當(dāng)循環(huán)計(jì)數(shù)器的變量命名為“i”是一種傳統(tǒng)做法攀圈,含義是increment(遞增)暴凑。
    for(var i=0;i<links.length;i++){
    ④改變行為
links[i].onclick=function(){         /*定義匿名函數(shù)*/
showPic(this);                            /*this代表links[i]*/
return false;                              /*禁用鏈接默認(rèn)行為*/
}                            

⑤完成JS函數(shù)

function prepareGallery(){
if(!document.getElementsByTagName||!document.getElementById)return false;
if(!document.getElementById(“imagegallery”))return false;
var galley=document.getElementById("imagegallery");
var links=gallery.getElementsByTagName("a");
for(var i=0;i<links.length;i++){
links[i].onclick=function(){
showPic(this); 
return false; 
}
}
}

(2)共享onload事件。
如果DOM不完整赘来,則測(cè)試的準(zhǔn)確性就無(wú)從談起搬设。因此應(yīng)該讓這個(gè)函數(shù)在網(wǎng)頁(yè)加載完畢之后立刻執(zhí)行。網(wǎng)頁(yè)加載完畢會(huì)觸發(fā)onload事件撕捍,必須把prepareGallery函數(shù)綁定在這個(gè)事件上:window.onload=prepareGallery;
但如果想讓兩個(gè)函數(shù)都在頁(yè)面加載完成是執(zhí)行拿穴,分別與onload綁定,只有最后那個(gè)函數(shù)會(huì)執(zhí)行忧风。一個(gè)最簡(jiǎn)單的解決方法:創(chuàng)建一個(gè)匿名函數(shù)來(lái)容納這兩個(gè)函數(shù)默色,再將該匿名函數(shù)與onload綁定。一個(gè)彈性最佳的解決方法:利用addLoadEvent函數(shù)狮腿。

function addLoadEvent(func){
var oldonload=window.onload;
if(typeof window.onload!='function'{
window.onload=func;
}else{
window.onload=function(){
oldonload();
func();
}
}
}

4 不要做太多假設(shè)

之前的代碼里用到了id屬性值等于placeholder和description的元素腿宰,但未對(duì)這些元素是否存在做任何檢查。假如我們要實(shí)現(xiàn)缘厢,只要placeholder圖片存在吃度,即使description元素不存在,切換顯示新圖片的操作也照常進(jìn)行贴硫。
檢查placeholder:
if(!document.getElementById("placeholder"))return false;
description:
if(document.getElementById("description"));這樣是可選的椿每,有則執(zhí)行伊者,無(wú)則忽略。
添加這兩條代碼后间护,即使HTML中沒(méi)有id=placeholder也不會(huì)出現(xiàn)JS錯(cuò)誤亦渗,但是會(huì)出現(xiàn)點(diǎn)擊鏈接沒(méi)任何響應(yīng),這意味著腳本不能平穩(wěn)退化汁尺。問(wèn)題在于prepareGallery函數(shù)做出了這樣的假設(shè):showPic函數(shù)肯定會(huì)正常返回法精。基于這一假設(shè)痴突,prepareGallery函數(shù)取消了onclick事件的默認(rèn)行為搂蜓。是否返回false值以取消onclick事件的默認(rèn)行為,應(yīng)該由showPic函數(shù)決定辽装。如果圖片切換成功帮碰,返回true;如果圖片切換不成功,返回false如迟。應(yīng)該在返回前驗(yàn)證showPic的返回值,以決定是否組織默認(rèn)行為攻走。如果showPic返回true,則返回false阻止默認(rèn)行為殷勘;如果showPic返回false,則返回true允許默認(rèn)行為。

function prepareGallery(){
if(!document.getElementsByTagName||!document.getElementById)return false;
if(!document.getElementById("imagegallery"))return false;
var gallery=document.getElementById("imagegallery");
var links=gallery.getElementsByTagName("a");
for(var i=0;i<links.length;i++){
links[i].onclick=function(){
return !showPic(this);  
}
}
}

5 優(yōu)化

showPic函數(shù)里仍存在一些需要處理的假設(shè)昔搂,如假設(shè)每個(gè)鏈接都有title屬性玲销。

if(whichpic.getAttribute("title") !=null)

作為一種簡(jiǎn)單的視覺(jué)反饋,把title不存在時(shí)的text設(shè)置為空字符串:

if(whichpic.getAttribute("title") !=null){
var text=whichpic.getAttribute("title");
} else {
var text="";
}
還可以寫成:
var text=whichpic.getAttribute("title")摘符?whichpic.getAttribute("title"):"";
這里的問(wèn)號(hào)叫三元操作符贤斜,其后是text的兩種取值:
variable=condition?if true:if false;

還可驗(yàn)證placeholder是否為圖片:

if(placeholder.nodeName!="IMG") return false;

注意nodeName總是返回一個(gè)大寫字母的值,即使元素在HTML中是小寫逛裤。
還可驗(yàn)證description的第一個(gè)子元素是文本:

if(description.firstChild.nodeType==3){
description.firstChild.nodeValue=text;
}

在實(shí)際工作中瘩绒,你要自己決定是否需要這些檢查,它們針對(duì)的是HTML中可能不在你控制范圍的內(nèi)情況带族。理想情況下不應(yīng)該對(duì)HTML的內(nèi)容和結(jié)構(gòu)做太多假設(shè)锁荔。

6 鍵盤訪問(wèn)

有些用戶不使用鼠標(biāo),使用鍵盤蝙砌,需調(diào)用onkeypress事件處理函數(shù)阳堕。要讓onkeypress和onclick觸發(fā)同樣的行為,可復(fù)制onclick代碼择克,或者links[i].onkeypress=links[i].onclick;但onkeypress很容易出問(wèn)題恬总。不過(guò)在幾乎所有瀏覽器里,用Tab鍵移動(dòng)到某個(gè)鏈接后按回車肚邢,也可觸發(fā)onclick氧秘。最好不要使用onkeypress溅呢,onclick對(duì)鍵盤訪問(wèn)的支持已很完美醋奠。最終代碼:

window.onload=prepareGallery;
function prepareGallery(){
if(!document.getElementsByTagName||!document.getElementById) return false;
if(!document.getElementById("imagegallery"))return false;
var gallery=document.getElementById("imagegallery");
var links=gallery.getElementsByTagName("a");
for(var i=0;i<links.length;i++){
links[i].onclick=function(){
return showPic(this)?false:true;
}
}
}
function showPic(whichpic){
    if(!document.getElementById("placeholder")) return false;
    var source = whichpic.getAttribute("href");
    var placeholder = document.getElementById("placeholder");
    if(placeholder.nodeName !="IMG") return false;
    placeholder.setAttribute("src",source);
    if(document.getElementById("description")){
    var text = whichpic.getAttribute("title")? whichpic.getAttribute("title"):"";
    var description = document.getElementById("description");
    if (description.firstChild.nodeType==3){
        description.firstChild.nodeValue = text;
    }
}
return true;
}

7 把JS和CSS結(jié)合起來(lái)##

DOM Core和HTML-DOM##

至此用到的DOM方法:getElementsByTagName、getElementById记劈、getAttribute、setAttribute都是DOM Core的組成部分并巍,并不專屬于JS目木,支持DOM的任何一種程序語(yǔ)言都可以使用它們。使用JS和DOM為HTML文件編寫腳本時(shí)懊渡,還有許多屬性可供選用刽射,如onclick,這些屬性屬于HTML-DOM。例如HTML-DOM提供了一個(gè)forms對(duì)象剃执,它可以把document.getElementsByTagName("form")簡(jiǎn)化成document.forms誓禁。HTML-DOM代碼通常比DOM Core代碼簡(jiǎn)短,但它們只能用來(lái)處理Web文檔肾档∧∏。可根據(jù)個(gè)人喜好和具體情況進(jìn)行選擇。

第7章 動(dòng)態(tài)創(chuàng)建標(biāo)記

網(wǎng)頁(yè)的結(jié)構(gòu)由標(biāo)記負(fù)責(zé)創(chuàng)建怒见,絕大多數(shù)JS函數(shù)只用來(lái)改變某些細(xì)節(jié)而不改變其底層結(jié)構(gòu)俗慈。但JS也可以用來(lái)改變網(wǎng)頁(yè)的結(jié)構(gòu)和內(nèi)容。

1 傳統(tǒng)方法

(1)document.write
HTML:

<body>
<script>
document.write("<p>This is inserted.</p>");
</script>
</body>

其最大缺點(diǎn)是違背了“行為應(yīng)該與表現(xiàn)分離”的原則遣耍。即使把語(yǔ)句挪到外部函數(shù)里闺阱,也仍然要在<body>部分添加<script>來(lái)調(diào)用。最好用外部JS文件去控制網(wǎng)頁(yè)行為舵变,避免在<body>部分亂用<script>,避免使用document.write方法酣溃。

(2)innerHTML屬性
類似于document.write方法,innerHTML屬性也是HTML專有屬性纪隙,不能用于任何其他標(biāo)記語(yǔ)言文檔赊豌。任何時(shí)候,標(biāo)準(zhǔn)的DOM都可以替代innerHTML绵咱,雖然往往需要多編寫一些代碼亿絮,但DOM提供了更高的精確性和更強(qiáng)大的功能。

2 DOM方法

DOM不僅可以獲取文檔內(nèi)容麸拄,還可以更新文檔內(nèi)容派昧。如setAttibute,注意它并未改變文檔的物理內(nèi)容拢切,只有用瀏覽器打開(kāi)文檔是才會(huì)看到效果變化蒂萎,這是因?yàn)闉g覽器實(shí)際顯示的是那棵DOM節(jié)點(diǎn)樹(shù),在瀏覽器看來(lái)淮椰,DOM節(jié)點(diǎn)樹(shù)才是文檔五慈。所以你不是在創(chuàng)建標(biāo)記纳寂,而是在改變DOM節(jié)點(diǎn)樹(shù)。
(1)createElement方法

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="testdiv">
</div>
</body>
</html>

想把一段文本插入到testdiv中泻拦,用DOM語(yǔ)言說(shuō)毙芜,就是要?jiǎng)?chuàng)建一個(gè)p節(jié)點(diǎn),并將其作為div節(jié)點(diǎn)的一個(gè)子節(jié)點(diǎn)争拐。用createElement創(chuàng)建:

var para = document.createElement("p");

任何時(shí)候腋粥,只要使用了createElement,把新創(chuàng)建的元素賦給一個(gè)變量總是個(gè)好主意架曹。雖然p已經(jīng)存在隘冲,但它還不是任何DOM節(jié)點(diǎn)樹(shù)的組成部門,這種情況稱為文檔碎片绑雄,但它已經(jīng)像其他節(jié)點(diǎn)一樣展辞,有自己的DOM屬性了。

(2)appendChild方法

parent.appendChild(child)  /*child不上引號(hào)*/
具體到上面的例子万牺,讓p稱為testdiv的子節(jié)點(diǎn):
var testdiv=document.getElementById("testdiv");
testdiv.appendChild(para);

雖然使用appendChild方法時(shí)罗珍,不必非得使用一些變量來(lái)引用父節(jié)點(diǎn)和子節(jié)點(diǎn),但這樣會(huì)提高代碼的可讀性脚粟。

(3)createTextNode方法
現(xiàn)在想把一些文本放入p元素覆旱,需要用createTextNode,語(yǔ)法與createElement類似珊楼。

var para = document.createElement("p");
var testdiv=document.getElementById("testdiv");
testdiv.appendChild(para);
var txt=document.createTextNode("Hello World");
para.createTextNode(txt);

appendChild方法還可以用來(lái)連接那些尚未成為文檔樹(shù)的節(jié)點(diǎn)通殃,所以可以先創(chuàng)建節(jié)點(diǎn)度液,再使用appendChild連接厕宗。

(4)一個(gè)更復(fù)雜的組合
如果要插入<p>This is <em>my</em> content.</p>,先分析節(jié)點(diǎn)樹(shù)再寫代碼:

window.onload=function(){
var para=document.createElement("p");
var txt1=document.createTextNode("This is ");
var emphasis=document.createElement("em");
var txt2=document.createTextNode("my ");
var txt3=document.createTextNode("content.");
para.appendChild(txt1);
para.appendChild(emphasis);
para.appendChild(txt3);
emphasis.appendChild(txt2);
var testdiv=document.getElementById("testdiv");
testdiv.appendChild(para);
}

3 重回圖片庫(kù)

之前圖片庫(kù)的HTML中有一個(gè)圖片和一段文字的存在只是為了讓DOM處理,那么用DOM方法來(lái)創(chuàng)建它們是最合適的選擇堕担。
需要完成的任務(wù):

  • 創(chuàng)建一個(gè)img元素節(jié)點(diǎn)
  • 設(shè)置這個(gè)節(jié)點(diǎn)的id屬性
  • 設(shè)置這個(gè)節(jié)點(diǎn)的src屬性
  • 設(shè)置這個(gè)節(jié)點(diǎn)的alt屬性
  • 創(chuàng)建一個(gè)p元素節(jié)點(diǎn)
  • 設(shè)置這個(gè)節(jié)點(diǎn)的id屬性
  • 創(chuàng)建一個(gè)文本節(jié)點(diǎn)
  • 把這個(gè)文本節(jié)點(diǎn)追加到p元素上
  • 把p元素和img元素插入到gallery.html文檔
var placeholder=document.createElement("img");
placeholder.setAttribute("id","placeholder");
placeholder.setAttribute("src","images/show.jpg");
placeholder.setAttribute("alt","my image gallery");
var description=document.createElement("p");
description.setAttribure("id","description");
var desctext=document.createTextNode("Choose an image");
description.appendChild(desctext);
document.body.appendChild(placeholder);
document.body.appendChild(description);

(1)在已有元素前插入一個(gè)新元素
原HTML文檔中圖片清單剛好在文檔最后已慢,所以把placeholder和description追加到body節(jié)點(diǎn)上,它們會(huì)出現(xiàn)在清單后面霹购。如果想把一個(gè)新元素插入到一個(gè)現(xiàn)有元素的前面佑惠,可用insertBefore()方法實(shí)現(xiàn):

parentElement.insertBefore(newElement,targetElement)

其實(shí)不用搞清楚parentElement是哪個(gè),因?yàn)閠argetElement的parentNode屬性值就是它齐疙。

gallery.parentNode.insertBefore(description,gallery);

(2)在現(xiàn)有方法后插入一個(gè)新元素
DOM沒(méi)有提供insertAfter方法膜楷,但完全可以用DOM方法編寫一個(gè)insertAfter方法。思路贞奋,查看目標(biāo)元素是不是parent的lastChild,如果是赌厅,直接appendChild;如果不是轿塔,就插入到目標(biāo)元素下一個(gè)兄弟元素的前面特愿。

function insertAfter(newElement,targetElement){
var parent=targetElement.parentNode;
if(parent.lastChild==targetElement){
parent.appendChild(newElement);
}else{
parent.insertBefore(newElement,targetElement.nextSibling);
}

(3)圖片庫(kù)二次改進(jìn)

4 Ajax

2005年發(fā)明仲墨,用于概括異步加載頁(yè)面內(nèi)容的技術(shù)。使用Ajax可以做到只更新頁(yè)面中的一小部分揍障,不必再次加載整個(gè)頁(yè)面目养。對(duì)頁(yè)面的請(qǐng)求以異步方式發(fā)送到服務(wù)器,服務(wù)器不會(huì)用整個(gè)頁(yè)面來(lái)響應(yīng)請(qǐng)求毒嫡,會(huì)在后來(lái)處理請(qǐng)求癌蚁,用戶此時(shí)仍能繼續(xù)瀏覽頁(yè)面并與頁(yè)面交互。
(1)XMLHttpRequest對(duì)象
它是Ajax技術(shù)的核心审胚,充當(dāng)瀏覽器的腳本與服務(wù)器之間的中間人匈勋。JS通過(guò)這個(gè)對(duì)象可以自己發(fā)送請(qǐng)求和處理響應(yīng)。由于不同瀏覽器實(shí)現(xiàn)該對(duì)象的方式不太一樣膳叨,因此需要為同一事情寫不同的代碼分支洽洁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市菲嘴,隨后出現(xiàn)的幾起案子饿自,更是在濱河造成了極大的恐慌,老刑警劉巖龄坪,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昭雌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡健田,警方通過(guò)查閱死者的電腦和手機(jī)烛卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)妓局,“玉大人总放,你說(shuō)我怎么就攤上這事『门溃” “怎么了局雄?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)存炮。 經(jīng)常有香客問(wèn)我炬搭,道長(zhǎng),這世上最難降的妖魔是什么穆桂? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任宫盔,我火速辦了婚禮,結(jié)果婚禮上享完,老公的妹妹穿的比我還像新娘灼芭。我一直安慰自己,他們只是感情好驼侠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布姿鸿。 她就那樣靜靜地躺著谆吴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苛预。 梳的紋絲不亂的頭發(fā)上句狼,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音热某,去河邊找鬼腻菇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昔馋,可吹牛的內(nèi)容都是我干的筹吐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼秘遏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丘薛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起邦危,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤洋侨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后倦蚪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體希坚,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年陵且,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裁僧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慕购,死狀恐怖聊疲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脓钾,我是刑警寧澤售睹,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布桩警,位于F島的核電站可训,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捶枢。R本人自食惡果不足惜握截,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烂叔。 院中可真熱鬧谨胞,春花似錦、人聲如沸蒜鸡。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至叶沛,卻和暖如春蒲讯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灰署。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工判帮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溉箕。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓晦墙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肴茄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子晌畅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容