JS也可以用來改變網(wǎng)頁的結構和內(nèi)容
一些傳統(tǒng)方法
document.write
document對象的write()方法可以方便快捷地把字符串插入到文檔里鲤竹。
如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<script type="text/javascript">
document.write("<p>This is inserted.</p>");
</script>
</body>
</html>
document.write的最大缺點就是違背了行為應該與表現(xiàn)分離的原則箫老。即使把document.write語句挪到外部函數(shù)里流译,也還是需要在標記的<body>部分使用<script>標簽才能調(diào)用那個函數(shù)逞怨。
例如:
function insertParagraph(text){
var str="<p>";
str+=text;
str+="</p>";
document.write(str);
}
-----------------------------
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<script type="text/javascript" src="example.js"></script>
<script type="text/javascript">
insertParagraph("This is inserted.");
</script>
</body>
</html>
PS:上面的這種寫法缺點有,很容易導致驗證錯誤福澡。MIME類型application/xhtml+xml與document.write不兼容骇钦,瀏覽器在呈現(xiàn)這種XHTML文檔時根本不會執(zhí)行document.write方法。
innerHTML屬性
現(xiàn)如今的瀏覽器幾乎都支持屬性innerHTML,這個屬性不是W3C DOM標準的組成部分竞漾,但現(xiàn)在已經(jīng)包含到HTML5規(guī)范中眯搭。innerHTML屬性可以用來讀寫某個給定元素的HTML內(nèi)容。
舉個例子就很容易理解了业岁。
<div id="testdiv">
<p>This is <em>my</em>content.</p>
</div>
從innerHTML屬性的角度來看鳞仙,id為testdiv的標記里面只有一個值為<p>This is <em>my</em>content.</p>的HTML字符串。
window.onload=function(){
var testdiv=document.getElementById("testdiv");
alert(testdiv.innerHTML);
}
很明顯笔时,innerHTML屬性無細節(jié)可言棍好,要想獲得細節(jié),就必須使用DOM方法和屬性允耿。標準化的DOM像手術刀一樣精細借笙,innerHTML屬性就像一把大錘那樣粗放。
當你需要把一大段HTML內(nèi)容插入網(wǎng)頁時较锡,innerHTML屬性更適用业稼,它既支持讀取,又支持寫入蚂蕴,你不僅可以用它來讀出元素的HTML內(nèi)容低散,還可以用它把HTML內(nèi)容寫入元素。
window.onload=function(){
var testdiv=document.getElementById("testdiv");
testdiv.innerHTML="<p>I inserted<em>this</em>content.</p>";
}
利用這個技術無法區(qū)分插入和替換一段HTML內(nèi)容骡楼,一旦使用了innerHTML屬性熔号,它的全部內(nèi)容將會被替換。innerHTML屬性時HTML專有屬性鸟整,不能用于任何其他標記語言文檔引镊。瀏覽器在呈現(xiàn)正宗的XHTML文檔時會直接忽略掉innerHTML屬性。
PS:在任何時候篮条,標準的DOM都可以用來替換innerHTML弟头。
DOM方法
DOM是文檔的表示。DOM所包含的信息與文檔里的信息一一對應兑燥。DOM是一條雙向的車道亮瓷。不僅可以獲取文檔的內(nèi)容,還可以更新文檔的內(nèi)容降瞳。
createElement方法
通過例子說明方法嘱支。
以下文檔里蚓胸,想把一段文本插入到testdiv元素。
<div id="testdiv">
</div>
用DOM的語言來說除师,就是想添加一個p元素節(jié)點沛膳。目前div已經(jīng)又一個屬性節(jié)點(子節(jié)點)。任務分為兩步:
- 創(chuàng)建一個新的元素 document.createElement(nodeName)
- 把這個新元素插入節(jié)點樹
實際操作過程如下:
var para=document.createElement("p");
//雖然創(chuàng)建出來p元素的引用汛聚,但它還不是任何一顆DOM節(jié)點樹的組成部分锹安。這種情況稱為文檔碎片(document fragment)
通過para.nodeName查看節(jié)點名稱
通過para.nodeType查看節(jié)點類型
appendChild方法
把新創(chuàng)建的節(jié)點插入某個文檔的節(jié)點樹的最簡單的辦法是,讓它成為這個文檔某個現(xiàn)在節(jié)點的一個子節(jié)點倚舀。
- 創(chuàng)建一個新的元素 document.createElement(nodeName)
- 把這個新元素插入節(jié)點樹 parent.appendChild(child)
var testdiv=document.getElementById("testdiv");
var para=document.createElement("p");
testdiv.appendChild(para);
createTextNode方法
你現(xiàn)在已經(jīng)創(chuàng)建出了一個元素節(jié)點并把它插入了文檔的節(jié)點樹叹哭,這個節(jié)點是一個空白的p元素。現(xiàn)在把一些文本放入這個p元素痕貌,創(chuàng)建文本節(jié)點风罩,使用createTextNode方法。
createTextNode語法與createElement很相似舵稠。document.createTextNode(text)
在p元素中插入文本節(jié)點超升。
var txt=document.createTextNode("Hello World");
para.appendChild(txt);
綜合上面的過程,完整代碼如下:
window.onload=function(){
var para=document.createElement("p");
var testdiv=document.getElementById("testdiv");
testdiv.appendChild(para);
var txt=document.createTextNode("Hello world");
para.appendChild(txt);
}
一個更復雜的組合
假如插入的內(nèi)容如下所示哺徊。
<p>This is <em>my</em>content.</p>
PS:自己寫完成室琢,再與課本對對看有什么不一樣。
var para=document.createElement("p");
var testdiv=document.getElementById("testdiv");
var txt=document.createTextNode("This is ");
var em=document.createElement("em");
var myTxt=document.createTextNode("my");
var contentTxt=document.createTextNode("content.");
para.appendChild(txt);
para.appendChild(em);
em.appendChild(myTxt);
para.appendChild(contentTxt);
testdiv.appendChild(para);
重回圖片庫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Image Gallery</title>
<link rel="stylesheet" type="text/css" href="picture.css" media="screen">
</head>
<script type="text/javascript" src="scripts/showpicture.js"></script>
<script type="text/javascript">
</script>
<body>
<h1>Snapshots</h1>
<ul id="imagegallery">
<li><a href="images/fireworks.jpg" title="A fireworks display">Fireworks</a></li>
<li><a href="images/coffee.jpg" title="A cup of black coffee">Coffee</a></li>
<li><a href="images/rose.jpg" title="A red,red rose" >Rose</a></li>
<li><a href="images/bigben.jpg" title="The famous clock">Big Ben</a></li>
</ul>
<!--下面這段代碼僅僅為showPic腳本服務落追,如果能把結構和行為徹底分開那最好不過了-->
![](images/placeholder.gif)
<p id="description">Choose an image</p>
<!------------->
</body>
</html>
PS: 既然上面標注的圖片和p只是為了讓DOM方法處理它們盈滴,那么讓DOM方法來創(chuàng)建它們才是最合適的選擇。我們需要做的就是1)將這些元素從文檔里刪掉淋硝。2)動態(tài)的將這些元素創(chuàng)建出來雹熬。3)把新創(chuàng)建的文檔元素插入到文檔中宽菜。
涉及到的方法有谣膳,創(chuàng)建元素節(jié)點createElement铅乡,設置屬性setAttribute继谚,創(chuàng)建文本節(jié)點createTextNode。
var placeholder=document.createElement("img");
placeholder.setAttribute("id","placeholder");
placeholder.setAttribute("src","images/placeholder.gif");
placeholder.setAttribute("alt","my image gallery");
var description=document.createElement("p");
description.setAttribute("id","description");
var desText=document.createTextNode("Choose an image");
document.getElementsByTagName("body")[0].appendChild(placeholder);
document.getElementsByTagName("body")[0].appendChild(description);
-------------------------------------------HTML-DOM屬性
document.body.appendChild(placeholder);
document.body.appendChild(description);
PS:這段代碼有個問題阵幸,假如在body里面還會動態(tài)元素的話花履,它就會處于文檔的中間而不會一直保持在最后的位置。
在已有元素前插入一個新元素
DOM提供了名為insertBefore()方法挚赊,這個方法將把一個新元素插入到一個現(xiàn)有元素的前面诡壁。調(diào)用此方法你必須告訴它三件事情。
- 新元素:你想插入的元素(newElement)
- 目標元素:你想把這個新元素插入到哪個元素(targetElement)之前
- 父元素:目標元素的父元素(parentElement)
語法是:parentElement.insertBefore(newElement,targetElement)
我們不必搞清楚父元素到底是哪個荠割,因為targetElement元素的parentNode就是它妹卿。既targetElement.parentNode=parentElement
比如說將圖片插入到清單之前旺矾,可以使用以下語句。
var gallery=document.getElementById("imagegallery");
gallery.parentNode.insertBefore(placeholder,gallery);
但是夺克,現(xiàn)在我們需要將圖片插入到清單的后面箕宙,用下面的方法。
在現(xiàn)有方法后插入一個新元素
PS:我想當然的認為铺纽,既然有在insertBefore方法柬帕,那肯定也有insertAfter方法,可是沒有狡门。但是我們可以自己實現(xiàn)陷寝。
function insertAfter(newElement,targetElement){
var parent=targetElement.parentNode;
if(parent.lastChild==targetElement){//目標元素是parent的最后一個元素,直接追加到parent上其馏。
parent.appendChild(newElement);
}else{//把新元素插入到目標元素和目標元素的下一個兄弟元素之間
parent.insertBefore(newElement,targetElement.nextSibling);
}
}
目標元素的下一個兄弟元素既目標元素targetElement.nextSibling
現(xiàn)在我們使用insertAfter函數(shù)盼铁,使用該方法改寫最后兩句話。
var gallery=document.getElementById("imagegallery");
insertAfter(placeholder,gallery);
整個代碼改寫如下,需要注意的是尝偎,我需要增加一些判斷饶火,判斷瀏覽器是否支持我使用的屬性,能保證平穩(wěn)的退化:
function preparePlaceholder(){
if(!document.createElement)return false;
if(!document.createTextNode)return false;
if(!document.getElementById)return false;
if(!document.getElementById("imagegallery"))return false;
var placeholder=document.createElement("img");
placeholder.setAttribute("id","placeholder");
placeholder.setAttribute("src","images/placeholder.gif");
placeholder.setAttribute("alt","my image gallery");
var description=document.createElement("p");
description.setAttribute("id","description");
var desText=document.createTextNode("Choose an image");
var gallery=document.getElementById("imagegallery");
insertAfter(placeholder,gallery);
insertAfter(placeholder,placeholder);
}
圖片庫二次改進版
- addLoadEvent函數(shù)
- insertAfter函數(shù)
- preparePlaceholder函數(shù)
- prepareGallery函數(shù)
- showPic函數(shù)
function showPic(whichpic) {
if(!document.getElementById("placeholder"))return false;
var source=whichpic.getAttribute("href");
var placeholder=document.getElementById("placeholder");
placeholder.setAttribute("src",source);
if(document.getElementById("description")){
var text=whichpic.getAttribute("title");
var description=document.getElementById("description");
description.firstChild.nodeValue=text;
}
return true;
}
function prepareGallery(){
if(!document.getElementsByTagName) return false;
if(!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);
}
}
}
function addLoadEvent(func){
var oldonload=window.onload;
if(typeof window.onload!='function'){
window.onload=func;
}else{
window.onload=function(){
oldonload();
func();
}
}
}
function insertAfter(newElement,targetElement){
var parent=targetElement.parentNode;
if(parent.lastChild==targetElement){
parent.appendChild(newElement);
}else{
parent.insertBefore(newElement,targetElement.nextSibling);
}
}
function preparePlaceholder(){
if(!document.createElement)return false;
if(!document.createTextNode)return false;
if(!document.getElementById)return false;
if(!document.getElementById("imagegallery"))return false;
var placeholder=document.createElement("img");
placeholder.setAttribute("id","placeholder");
placeholder.setAttribute("src","images/placeholder.gif");
placeholder.setAttribute("alt","my image gallery");
var description=document.createElement("p");
description.setAttribute("id","description");
var descText=document.createTextNode("Choose an image");
description.appendChild(descText);
var gallery=document.getElementById("imagegallery");
insertAfter(placeholder,gallery);
insertAfter(description,placeholder);
}
addLoadEvent(preparePlaceholder);
addLoadEvent(prepareGallery);
Ajax
上面的例子致扯,將某些節(jié)點放到JS中動態(tài)的添加肤寝。假如頁面只更新了一小部分,那怎樣才能真正得到原來并不存在于初始頁面中的內(nèi)容呢抖僵?
使用Ajax就可以做到只更新頁面中的一小部分內(nèi)容鲤看。它的主要優(yōu)勢就是對頁面的請求以異步方式發(fā)送到服務器。Ajax技術的核心就是XMLHttpRequest對象耍群,這個對象充當著瀏覽器中的腳本(客戶端)與服務器間的中間人的角色义桂。以往的請求都由瀏覽器發(fā)出,而JS通過這個對象可以自己發(fā)送請求蹈垢,同時也自己處理響應慷吊。
雖然XMLHttpRequest得到了幾乎所有現(xiàn)代瀏覽器的支持。但問題是曹抬,不同瀏覽器實現(xiàn)XMLHttpRequest對象的方式不太一樣溉瓶。為了保證跨瀏覽器,你不得不為同樣的事情寫不同的代碼分支谤民。
不同的IE版本中使用的XMLHTTP對象也不完全相同堰酿。為了兼容所有的瀏覽器,getHTTPObject函數(shù)要這樣寫:
function getHTTPObject(){
if(typeof XMLHttpRequest == "undefined")
XMLHttpRequest=function(){
try{
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
}catch(e){}
try{
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}catch(e){}
try{
return new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){}
return false;
}
return new XMLHttpRequest();
}
當你腳本要使用XMLHttpRequest對象時张足,可以將這個新對象直接賦值給一個變量触创。var request=getHttpObject();
在這之前,我需要搞懂兩點为牍。
- typeof的用法:可以使用 typeof 操作符來檢測變量的數(shù)據(jù)類型哼绑。typeof 一個沒有值的變量會返回 undefined顺饮。
- XMLHttpRequest=function(){}是什么用法?相當于檢測XMLHttpRequest對象是否存在凌那。
XMLHttpRequest有很多方法兼雄,最有用的是open方法,用來指定服務器上將要訪問的文件帽蝶,指定請求類型:GET赦肋、POST或SEND。
function getNewContent(){
var request=getHTTPObject():
if(request){
//發(fā)送GET請求励稳,請求與ajax.html文件位于同一目錄的example.txt文件
request.open("GET","example.txt",true);
//onreadystatechange是一個事件處理函數(shù)佃乘,服務器響應時被觸發(fā)
request.onreadystatechange=function(){
if(request.readyState==4){
var para=document.createElement("p");
var txt=document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById("new").appendChild(para);
}
};
request.send(null);
}else{
alert("Sorry,your browser doesn\'t support XMLHttpRequest");
}
}
addLoadEvent(getNewContent);
服務器在向XMLHttpRequest對象發(fā)回響應時,該對象有許多屬性可用驹尼,readyState有5個可能的值瓶珊。
- 0表示未初始化
- 1表示正在加載
- 2表示加載完畢
- 3 表示正在交互
- 4表示完成 // 可以直接使用服務器發(fā)送回來的數(shù)據(jù)扳碍。
可以通過responseText和responseXML屬性來訪問發(fā)送回來的數(shù)據(jù)。
- responseText用于保存文本字符串形式的數(shù)據(jù)
- responseXML用于保存Content-Type頭部中指定“text/xml”的數(shù)據(jù),其實是一個DocumentFragment對象胸遇,可以使用DOM來處理這個對象交洗。
重做下例子送粱,在谷歌運行發(fā)出現(xiàn)以下錯誤栓袖,safari就可以:
PS:原因是有些瀏覽器限制Ajax的使用協(xié)議,在Chrome中亏吝,如果你使用了file://協(xié)議從自己的硬盤中加載example.txt岭埠,就會看到Cross origin requests are only supported for protocol schemes的錯誤。
注意一點:異步請求有個容易忽略的點就是腳本在發(fā)送XMLHttpRequest請求后蔚鸥,仍然會繼續(xù)執(zhí)行惜论,不會等待響應返回。
Ajax的特色就是減少重復加載頁面的次數(shù)止喷,但是缺少狀態(tài)記錄的技術會與瀏覽器的一些使用慣例產(chǎn)生沖突馆类,導致用戶無法使用后退按鈕或者無法為特定狀態(tài)下的頁面添加書簽。
漸進增強與Ajax
構建Ajax網(wǎng)站启盛,從一開始就從構建一個常規(guī)網(wǎng)站開始蹦掐,然后再使用Ajax。
Hijax
Hijax指漸進增強的使用Ajax僵闯。
Ajax應用主要依賴后臺服務器,實際上是服務器端的腳本語言完成了絕大部分工作藤滥。XMLHttpRequest對象作為瀏覽器與服務器之間的“中間人”鳖粟,它只是負責傳遞請求和響應。如果把這個中間人請開拙绊。瀏覽器與服務器之間的請求和響應應該繼續(xù)完成向图。
構建表單的傳統(tǒng)方式泳秀。
- 讓表單把整個頁面都提交到服務器,然后服務器再發(fā)回來包含反饋的新頁面榄攀。所有處理操作都在服務器上完成嗜傅,用戶在表單中輸入的數(shù)據(jù)則由服務器負責取得并保存在數(shù)據(jù)庫里的數(shù)據(jù)進行比較,看是不是真的存在這個用戶檩赢。
Hijax的方式
- 攔截提交表單的請求吕嘀,讓XMLHttpRequest請求來代為發(fā)送。提交表單觸發(fā)的是submit事件贞瞒,因此只要通過onsubmit事件處理函數(shù)捕獲該事件偶房,就可以取消它的默認操作,然后代之以一個新的操作:通過XMLHttpRequest對象向服務器發(fā)送數(shù)據(jù)军浆。
Ajax應用主要依賴于服務器端處理棕洋,而非客戶端處理。
具體的Hijax實例參考筆記9: