JavaScript的組成
JavaScript基礎(chǔ)分為三個(gè)部分:
ECMAScript:JavaScript的語(yǔ)法標(biāo)準(zhǔn)。包括變量、表達(dá)式、運(yùn)算符、函數(shù)、if語(yǔ)句、for語(yǔ)句等。
DOM:文檔對(duì)象模型(Document object Model),操作網(wǎng)頁(yè)上的元素的API。比如讓盒子移動(dòng)、變色、輪播圖等。
BOM:瀏覽器對(duì)象模型,操作瀏覽器部分功能的API。比如讓瀏覽器自動(dòng)滾動(dòng)。
節(jié)點(diǎn)
節(jié)點(diǎn)(Node):構(gòu)成 HTML 網(wǎng)頁(yè)的最基本單元。網(wǎng)頁(yè)中的每一個(gè)部分都可以稱(chēng)為是一個(gè)節(jié)點(diǎn),比如:html標(biāo)簽、屬性、文本、注釋、整個(gè)文檔等都是一個(gè)節(jié)點(diǎn)。
雖然都是節(jié)點(diǎn),但是實(shí)際上他們的具體類(lèi)型是不同的。常見(jiàn)節(jié)點(diǎn)分為四類(lèi):
文檔節(jié)點(diǎn)(文檔):整個(gè) HTML 文檔。整個(gè) HTML 文檔就是一個(gè)文檔節(jié)點(diǎn)。
元素節(jié)點(diǎn)(標(biāo)簽):HTML標(biāo)簽。
屬性節(jié)點(diǎn)(屬性):元素的屬性。
文本節(jié)點(diǎn)(文本):HTML標(biāo)簽中的文本內(nèi)容(包括標(biāo)簽之間的空格、換行)。
節(jié)點(diǎn)的類(lèi)型不同,屬性和方法也都不盡相同。所有的節(jié)點(diǎn)都是Object。
什么是DOM
DOM:Document Object Model,文檔對(duì)象模型。DOM 為文檔提供了結(jié)構(gòu)化表示,并定義了如何通過(guò)腳本來(lái)訪問(wèn)文檔結(jié)構(gòu)。目的其實(shí)就是為了能讓js操作html元素而制定的一個(gè)規(guī)范。
DOM就是由節(jié)點(diǎn)組成的。
解析過(guò)程:HTML加載完畢,渲染引擎會(huì)在內(nèi)存中把HTML文檔,生成一個(gè)DOM樹(shù),getElementById是獲取內(nèi)中DOM上的元素節(jié)點(diǎn)。然后操作的時(shí)候修改的是該元素的屬性。
DOM樹(shù):(一切都是節(jié)點(diǎn))
DOM的數(shù)據(jù)結(jié)構(gòu)如下:

上圖可知,在HTML當(dāng)中,一切都是節(jié)點(diǎn)(非常重要)。節(jié)點(diǎn)的分類(lèi),在上一段中,已經(jīng)講了。
整個(gè)html文檔就是一個(gè)文檔節(jié)點(diǎn)。所有的節(jié)點(diǎn)都是Object。
DOM可以做什么
找對(duì)象(元素節(jié)點(diǎn))
設(shè)置元素的屬性值
設(shè)置元素的樣式
動(dòng)態(tài)創(chuàng)建和刪除元素
事件的觸發(fā)響應(yīng):事件源、事件、事件的驅(qū)動(dòng)程序
元素節(jié)點(diǎn)的獲取
DOM節(jié)點(diǎn)的獲取方式其實(shí)就是獲取事件源的方式。關(guān)于事件,上一篇文章中已經(jīng)講到了。
想要操作元素節(jié)點(diǎn),必須首先要找到該節(jié)點(diǎn)。有三種方式可以獲取DOM節(jié)點(diǎn):
var div1=document.getElementById("box1");//方式一:通過(guò) id 獲取 一個(gè) 元素節(jié)點(diǎn)(為什么是一個(gè)呢?因?yàn)?id 是唯一的)
va rarr1=document.getElementsByTagName("div");//方式二:通過(guò) 標(biāo)簽名 獲取 元素節(jié)點(diǎn)數(shù)組,所以有s
var arr2=document.getElementsByClassName("hehe");//方式三:通過(guò) 類(lèi)名 獲取 元素節(jié)點(diǎn)數(shù)組,所以有s
既然方式二、方式三獲取的是標(biāo)簽數(shù)組,那么習(xí)慣性是先遍歷之后再使用。
特殊情況:數(shù)組中的值只有1個(gè)。即便如此,這一個(gè)值也是包在數(shù)組里的。這個(gè)值的獲取方式如下:
document.getElementsByTagName("div1")[0];//取數(shù)組中的第一個(gè)元素
document.getElementsByClassName("hehe")[0];//取數(shù)組中的第一個(gè)元素
DOM訪問(wèn)關(guān)系的獲取
DOM的節(jié)點(diǎn)并不是孤立的,因此可以通過(guò)DOM節(jié)點(diǎn)之間的相對(duì)關(guān)系對(duì)它們進(jìn)行訪問(wèn)。如下:

節(jié)點(diǎn)的訪問(wèn)關(guān)系,是以屬性的方式存在的。
JS中的父子兄訪問(wèn)關(guān)系:

這里我們要重點(diǎn)知道parentNode和children這兩個(gè)屬性的用法。下面分別介紹。
獲取父節(jié)點(diǎn)
調(diào)用者就是節(jié)點(diǎn)。一個(gè)節(jié)點(diǎn)只有一個(gè)父節(jié)點(diǎn),調(diào)用方式就是
? ? 節(jié)點(diǎn).parentNode
獲取兄弟節(jié)點(diǎn)
1、下一個(gè)節(jié)點(diǎn) | 下一個(gè)元素節(jié)點(diǎn):
Sibling的中文是兄弟。
(1)nextSibling:
火狐、谷歌、IE9+版本:都指的是下一個(gè)節(jié)點(diǎn)(包括標(biāo)簽、空文檔和換行節(jié)點(diǎn))。
IE678版本:指下一個(gè)元素節(jié)點(diǎn)(標(biāo)簽)。
(2)nextElementSibling:
火狐、谷歌、IE9+版本:都指的是下一個(gè)元素節(jié)點(diǎn)(標(biāo)簽)。
總結(jié):為了獲取下一個(gè)元素節(jié)點(diǎn),我們可以這樣做:在IE678中用nextSibling,在火狐谷歌IE9+以后用nextElementSibling,于是,綜合這兩個(gè)屬性,可以這樣寫(xiě):
? ? 下一個(gè)兄弟節(jié)點(diǎn)=節(jié)點(diǎn).nextElementSibling||節(jié)點(diǎn).nextSibling
2、前一個(gè)節(jié)點(diǎn) | 前一個(gè)元素節(jié)點(diǎn):
previous的中文是:前一個(gè)。
(1)previousSibling:
火狐、谷歌、IE9+版本:都指的是前一個(gè)節(jié)點(diǎn)(包括標(biāo)簽、空文檔和換行節(jié)點(diǎn))。
IE678版本:指前一個(gè)元素節(jié)點(diǎn)(標(biāo)簽)。
(2)previousElementSibling:
火狐、谷歌、IE9+版本:都指的是前一個(gè)元素節(jié)點(diǎn)(標(biāo)簽)。
總結(jié):為了獲取前一個(gè)元素節(jié)點(diǎn),我們可以這樣做:在IE678中用previousSibling,在火狐谷歌IE9+以后用previousElementSibling,于是,綜合這兩個(gè)屬性,可以這樣寫(xiě):
? ? 前一個(gè)兄弟節(jié)點(diǎn)=節(jié)點(diǎn).previousElementSibling||節(jié)點(diǎn).previousSibling
3、補(bǔ)充:獲得任意一個(gè)兄弟節(jié)點(diǎn):
? ? 節(jié)點(diǎn)自己.parentNode.children[index];//隨意得到兄弟節(jié)點(diǎn)
獲取單個(gè)的子節(jié)點(diǎn)
1、第一個(gè)子節(jié)點(diǎn) | 第一個(gè)子元素節(jié)點(diǎn):
(1)firstChild:
火狐、谷歌、IE9+版本:都指的是第一個(gè)子節(jié)點(diǎn)(包括標(biāo)簽、空文檔和換行節(jié)點(diǎn))。
IE678版本:指第一個(gè)子元素節(jié)點(diǎn)(標(biāo)簽)。
(2)firstElementChild:
火狐、谷歌、IE9+版本:都指的是第一個(gè)子元素節(jié)點(diǎn)(標(biāo)簽)。
總結(jié):為了獲取第一個(gè)子元素節(jié)點(diǎn),我們可以這樣做:在IE678中用firstChild,在火狐谷歌IE9+以后用firstElementChild,于是,綜合這兩個(gè)屬性,可以這樣寫(xiě):
? ? 第一個(gè)子元素節(jié)點(diǎn)=節(jié)點(diǎn).firstElementChild||節(jié)點(diǎn).firstChild
2、最后一個(gè)子節(jié)點(diǎn) | 最后一個(gè)子元素節(jié)點(diǎn):
(1)lastChild:
火狐、谷歌、IE9+版本:都指的是最后一個(gè)子節(jié)點(diǎn)(包括標(biāo)簽、空文檔和換行節(jié)點(diǎn))。
IE678版本:指最后一個(gè)子元素節(jié)點(diǎn)(標(biāo)簽)。
(2)lastElementChild:
火狐、谷歌、IE9+版本:都指的是最后一個(gè)子元素節(jié)點(diǎn)(標(biāo)簽)。
總結(jié):為了獲取最后一個(gè)子元素節(jié)點(diǎn),我們可以這樣做:在IE678中用lastChild,在火狐谷歌IE9+以后用lastElementChild,于是,綜合這兩個(gè)屬性,可以這樣寫(xiě):
? ? 最后一個(gè)子元素節(jié)點(diǎn)=節(jié)點(diǎn).lastElementChild||節(jié)點(diǎn).lastChild
獲取所有的子節(jié)點(diǎn)
(1)childNodes:標(biāo)準(zhǔn)屬性。返回的是指定元素的子節(jié)點(diǎn)的集合(包括元素節(jié)點(diǎn)、所有屬性、文本節(jié)點(diǎn))。是W3C的親兒子。
火狐 谷歌等高本版會(huì)把換行也看做是子節(jié)點(diǎn)。
用法:
? ? 子節(jié)點(diǎn)數(shù)組=父節(jié)點(diǎn).childNodes;//獲取所有節(jié)點(diǎn)。
(2)children:非標(biāo)準(zhǔn)屬性。返回的是指定元素的子元素節(jié)點(diǎn)的集合?!局匾?/p>
它只返回HTML節(jié)點(diǎn),甚至不返回文本節(jié)點(diǎn)。
在IE6/7/8中包含注釋節(jié)點(diǎn)(在IE678中,注釋節(jié)點(diǎn)不要寫(xiě)在里面)。
雖然不是標(biāo)準(zhǔn)的DOM屬性,但它和innerHTML方法一樣,得到了幾乎所有瀏覽器的支持。
用法:(用的最多)
? ? 子節(jié)點(diǎn)數(shù)組=父節(jié)點(diǎn).children;//獲取所有節(jié)點(diǎn)。用的最多。
DOM節(jié)點(diǎn)的操作(重要)
上一段的內(nèi)容:節(jié)點(diǎn)的訪問(wèn)關(guān)系都是屬性。
本段的內(nèi)容:節(jié)點(diǎn)的操作都是函數(shù)(方法)。
創(chuàng)建節(jié)點(diǎn)
格式如下:
? ? 新的標(biāo)簽(元素節(jié)點(diǎn))=document.createElement("標(biāo)簽名");
比如,如果我們想創(chuàng)建一個(gè)li標(biāo)簽,或者是創(chuàng)建一個(gè)不存在的adbc標(biāo)簽,可以這樣做:
<scripttype="text/javascript">
var a1=document.createElement("li");//創(chuàng)建一個(gè)li標(biāo)簽
var a2=document.createElement("adbc");//創(chuàng)建一個(gè)不存在的標(biāo)簽
console.log(a1);
console.log(a2);
console.log(typeofa1);
console.log(typeofa2);
</script>
打印結(jié)果:

插入節(jié)點(diǎn)
插入節(jié)點(diǎn)有兩種方式,它們的含義是不同的。
方式1:
? ? 父節(jié)點(diǎn).appendChild(新的子節(jié)點(diǎn));
解釋?zhuān)焊腹?jié)點(diǎn)的最后插入一個(gè)新的子節(jié)點(diǎn)。
方式2:
? ? 父節(jié)點(diǎn).insertBefore(新的子節(jié)點(diǎn),作為參考的子節(jié)點(diǎn))
解釋?zhuān)?/p>
在參考節(jié)點(diǎn)前插入一個(gè)新的節(jié)點(diǎn)。
如果參考節(jié)點(diǎn)為null,那么他將在父節(jié)點(diǎn)里面的最后插入一個(gè)子節(jié)點(diǎn)。

我們可以看到,li標(biāo)簽確實(shí)被插入到了box1標(biāo)簽的里面,和box2并列了。
方式2的舉例:

我們可以看到,b1標(biāo)簽被插入到了box1標(biāo)簽的里面,和a1標(biāo)簽并列,在a1標(biāo)簽的前面。
特別強(qiáng)調(diào):
關(guān)于方式1的appendChild方法,這里要強(qiáng)調(diào)一下。比如,現(xiàn)在有下面這樣一個(gè)div結(jié)構(gòu):
<divclass="box11">
<divclass="box12">生命壹號(hào)</div>
</div>
<divclass="box21">
<divclass="box22">永不止步</div>
</div>
上方結(jié)構(gòu)中,子盒子box12是在父親box11里的,子盒子box22是在父親box21里面的。現(xiàn)在,如果我調(diào)用方法box11.appendChild(box22),最后產(chǎn)生的結(jié)果是:box22會(huì)跑到box11中(也就是說(shuō),box22不在box21里面了)。這是一個(gè)很神奇的事情:

格式如下:
? ? 父節(jié)點(diǎn).removeChild(子節(jié)點(diǎn));
解釋?zhuān)?b>用父節(jié)點(diǎn)刪除子節(jié)點(diǎn)。必須要指定是刪除哪個(gè)子節(jié)點(diǎn)。
如果我想刪除自己這個(gè)節(jié)點(diǎn),可以這么做:
? ? node1.parentNode.removeChild(node1);
復(fù)制節(jié)點(diǎn)(克隆節(jié)點(diǎn))
格式如下:
? ? 要復(fù)制的節(jié)點(diǎn).cloneNode();//括號(hào)里不帶參數(shù)和帶參數(shù)false,效果是一樣的。
? ? 要復(fù)制的節(jié)點(diǎn).cloneNode(true);
括號(hào)里帶不帶參數(shù),效果是不同的。解釋如下:
不帶參數(shù)/帶參數(shù)false:只復(fù)制節(jié)點(diǎn)本身,不復(fù)制子節(jié)點(diǎn)。
帶參數(shù)true:既復(fù)制節(jié)點(diǎn)本身,也復(fù)制其所有的子節(jié)點(diǎn)。
我們可以獲取節(jié)點(diǎn)的屬性值、設(shè)置節(jié)點(diǎn)的屬性值、刪除節(jié)點(diǎn)的屬性。
我們就統(tǒng)一拿下面這個(gè)標(biāo)簽來(lái)舉例:
? ? <imgsrc="images/1.jpg"class="image-box"title="美女圖片"alt="地鐵一瞥"id="a1">
下面分別介紹。
1、獲取節(jié)點(diǎn)的屬性值
方式1:
? ? 元素節(jié)點(diǎn).屬性名;
? ? 元素節(jié)點(diǎn)[屬性名];
舉例:(獲取節(jié)點(diǎn)的屬性值)
<body>
<imgsrc="images/1.jpg"class="image-box"title="美女圖片"alt="地鐵一瞥"id="a1">
<scripttype="text/javascript">
var myNode=document.getElementsByTagName("img")[0];
console.log(myNode.src);
console.log(myNode.className);//注意,是className,不是class
console.log(myNode.title);
console.log("------------");
console.log(myNode["src"]);
console.log(myNode["className"]);//注意,是className,不是class
console.log(myNode["title"]);
</script>
</body>
上方代碼中的img標(biāo)簽,有各種屬性,我們可以逐一獲取,打印結(jié)果如下:

方式2:
? ? 元素節(jié)點(diǎn).getAttribute("屬性名稱(chēng)");
舉例:
console.log(myNode.getAttribute("src"));
console.log(myNode.getAttribute("class"));//注意是class,不是className
console.log(myNode.getAttribute("title"));
打印結(jié)果:

方式1和方式2的區(qū)別在于:前者是直接操作標(biāo)簽,后者是把標(biāo)簽作為DOM節(jié)點(diǎn)。推薦方式2。
2、設(shè)置節(jié)點(diǎn)的屬性值
方式1舉例:(設(shè)置節(jié)點(diǎn)的屬性值)
myNode.src="images/2.jpg"http://修改src的屬性值
myNode.className="image2-box";//修改class的name
方式2:
? ? 元素節(jié)點(diǎn).setAttribute(屬性名,新的屬性值);
方式2舉例:(設(shè)置節(jié)點(diǎn)的屬性值)
myNode.setAttribute("src","images/3.jpg");
myNode.setAttribute("class","image3-box");
myNode.setAttribute("id","你好");
3、刪除節(jié)點(diǎn)的屬性
格式:
? ? 元素節(jié)點(diǎn).removeAttribute(屬性名);
舉例:(刪除節(jié)點(diǎn)的屬性)
myNode.removeAttribute("class");
myNode.removeAttribute("id");
總結(jié):
獲取節(jié)點(diǎn)的屬性值和設(shè)置節(jié)點(diǎn)的屬性值,都有兩種方式,但這兩種方式是有區(qū)別的。
方式一的元素節(jié)點(diǎn).屬性和元素節(jié)點(diǎn)[屬性]:綁定的屬性值不會(huì)出現(xiàn)在標(biāo)簽上。
方式二的get/set/removeAttribut: 綁定的屬性值會(huì)出現(xiàn)在標(biāo)簽上。
這其實(shí)很好理解,方式一操作的是屬性而已,方式二操作的是標(biāo)簽本身。
另外,需要注意的是:這兩種方式不能交換使用,get值和set值必須使用用一種方法。
舉例:
<body>
<divid="box"title="主體"class="asdfasdfadsfd">我愛(ài)你中國(guó)</div>
<script>
var div=document.getElementById("box");
//采用方式一進(jìn)行set
div.aaaa="1111";
console.log(div.aaaa);//打印結(jié)果:1111。可以打印出來(lái),但是不會(huì)出現(xiàn)在標(biāo)簽上
//采用方式二進(jìn)行set
div.setAttribute("bbbb","2222");//bbbb作為新增的屬性,會(huì)出現(xiàn)在標(biāo)簽上
console.log(div.getAttribute("aaaa"));//打印結(jié)果:null。因?yàn)榉绞揭坏膕et,無(wú)法采用方式二進(jìn)行g(shù)et。
console.log(div.bbbb);//打印結(jié)果:undefined。因?yàn)榉绞蕉膕et,無(wú)法采用方式一進(jìn)行g(shù)et。
</script>
</body>
DOM對(duì)象的屬性
DOM對(duì)象的屬性和HTML的標(biāo)簽屬性幾乎是一致的。例如:src、title、className、href等。
innerHTML和innerText的區(qū)別
value:標(biāo)簽的value屬性。
innerHTML:雙閉合標(biāo)簽里面的內(nèi)容(識(shí)別標(biāo)簽)。
innerText:雙閉合標(biāo)簽里面的內(nèi)容(不識(shí)別標(biāo)簽)。(老版本的火狐用textContent)
獲取內(nèi)容舉例:
如果我們想獲取innerHTML和innerText里的內(nèi)容,看看會(huì)如何:(innerHTML會(huì)獲取到標(biāo)簽本身,而innerText則不會(huì))

修改內(nèi)容舉例:(innerHTML會(huì)修改標(biāo)簽本身,而innerText則不會(huì))

nodeType屬性
這里講一下nodeType屬性。
nodeType == 1? 表示的是元素節(jié)點(diǎn)(標(biāo)簽) 。記?。涸鼐褪菢?biāo)簽。
nodeType == 2? 表示是屬性節(jié)點(diǎn)。
nodeType == 3? 是文本節(jié)點(diǎn)。
nodeType、nodeName、nodeValue
我們那下面這個(gè)標(biāo)簽來(lái)舉例:
<divid="box"value="111">
?? 生命壹號(hào)
</div>
上面這個(gè)標(biāo)簽就包含了三種節(jié)點(diǎn):
元素節(jié)點(diǎn)(標(biāo)簽)
屬性節(jié)點(diǎn)
文本節(jié)點(diǎn)
獲取這三個(gè)節(jié)點(diǎn)的方式如下:
var element=document.getElementById("box1");//獲取元素節(jié)點(diǎn)(標(biāo)簽)
var attribute=element.getAttributeNode("id");//獲取box1的屬性節(jié)點(diǎn)
var txt=element.firstChild;//獲取box1的文本節(jié)點(diǎn)
var value=element.getAttribute("id");//獲取id的屬性值
console.log(element);
console.log("--------------");
console.log(attribute);
console.log("--------------");
console.log(txt);
console.log("--------------");
console.log(value);
打印結(jié)果如下:

既然這三個(gè)都是節(jié)點(diǎn),如果我想獲取它們的nodeType、nodeName、nodeValue,代碼如下:
var element=document.getElementById("box1");//獲取元素節(jié)點(diǎn)(標(biāo)簽)
var attribute=element.getAttributeNode("id");//獲取box1的屬性節(jié)點(diǎn)
var txt=element.firstChild;//獲取box1的文本節(jié)點(diǎn)
//獲取nodeType
console.log(element.nodeType);//1
console.log(attribute.nodeType);//2
console.log(txt.nodeType);//3
console.log("--------------");
//獲取nodeName
console.log(element.nodeName);//DIV
console.log(attribute.nodeName);//id
console.log(txt.nodeName);//#text
console.log("--------------");
//獲取nodeValue
console.log(element.nodeValue);//null
console.log(attribute.nodeValue);//box1
console.log(txt.nodeValue);//生命壹號(hào)
打印結(jié)果如下:

文檔的加載
瀏覽器在加載一個(gè)頁(yè)面時(shí),是按照自上向下的順序加載的,讀取到一行就運(yùn)行一行。如果將script標(biāo)簽寫(xiě)到頁(yè)面的上邊,在代碼執(zhí)行時(shí),頁(yè)面還沒(méi)有加載,頁(yè)面沒(méi)有加載DOM對(duì)象也沒(méi)有加載,會(huì)導(dǎo)致無(wú)法獲取到DOM對(duì)象。
onload 事件:
onload 事件會(huì)在整個(gè)頁(yè)面加載完成之后才觸發(fā)。為 window 綁定一個(gè)onload事件,該事件對(duì)應(yīng)的響應(yīng)函數(shù)將會(huì)在頁(yè)面加載完成之后執(zhí)行,這樣可以確保我們的代碼執(zhí)行時(shí)所有的DOM對(duì)象已經(jīng)加載完畢了。
代碼舉例:
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8"/>
<title></title>
<scripttype="text/javascript">
// 【方式一:先加載,后執(zhí)行】這段 js 代碼是寫(xiě)在 <head> 標(biāo)簽里的,所以建議放在 window.onload 里面。
window.onload=function() {
// 獲取id為btn的按鈕
varbtn=document.getElementById("btn");
// 為按鈕綁定點(diǎn)擊事件
btn.onclick=function() {
alert("hello");
? ? ?? };
? ?? };
</script>
</head>
<body>
<buttonid="btn">點(diǎn)我一下</button>
<scripttype="text/javascript">
// 【方式二:后加載,后執(zhí)行】這段 js 代碼是寫(xiě)在 <body> 標(biāo)簽里的,代碼的位置是處在頁(yè)面的下方。這么做,也可以確保:在頁(yè)面加載完畢后,再執(zhí)行 js 代碼。
// 獲取id為btn的按鈕
var btn=document.getElementById("btn");
// 為按鈕綁定點(diǎn)擊事件
btn.onclick=function() {
alert("hello");
? ?? };
</script>
</body>
</html>
上方代碼中,方式一和方式二均可以確保:在頁(yè)面加載完畢后,再執(zhí)行 js 代碼。