[筆記8]JavaScript DOM編程藝術_動態(tài)創(chuàng)建標記

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>
屏幕快照 2017-02-03 下午11.13.04.png

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>
屏幕快照 2017-02-04 下午10.57.46.png

從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就可以:

屏幕快照 2017-02-07 下午10.39.12.png

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:

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乒融,一起剝皮案震驚了整個濱河市掰盘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赞季,老刑警劉巖庆杜,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碟摆,居然都是意外死亡晃财,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門典蜕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來断盛,“玉大人,你說我怎么就攤上這事愉舔「置停” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵轩缤,是天一觀的道長命迈。 經(jīng)常有香客問我,道長火的,這世上最難降的妖魔是什么壶愤? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮馏鹤,結果婚禮上征椒,老公的妹妹穿的比我還像新娘。我一直安慰自己湃累,他們只是感情好勃救,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布碍讨。 她就那樣靜靜地躺著,像睡著了一般蒙秒。 火紅的嫁衣襯著肌膚如雪勃黍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天晕讲,我揣著相機與錄音覆获,去河邊找鬼。 笑死益兄,一個胖子當著我的面吹牛锻梳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播净捅,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疑枯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛔六?” 一聲冷哼從身側響起荆永,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎国章,沒想到半個月后具钥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡液兽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年骂删,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片四啰。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宁玫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柑晒,到底是詐尸還是另有隱情欧瘪,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布匙赞,位于F島的核電站佛掖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涌庭。R本人自食惡果不足惜芥被,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脾猛。 院中可真熱鬧撕彤,春花似錦、人聲如沸猛拴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愉昆。三九已至职员,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跛溉,已是汗流浹背焊切。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芳室,地道東北人专肪。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像堪侯,于是被迫代替她去往敵國和親嚎尤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

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