title: 《DOM編程藝術(shù)》六、充實(shí)文檔內(nèi)容
date: 2017-06-03 10:28:24
tags: DOM編程藝術(shù)
這一章將繼續(xù)在實(shí)踐中應(yīng)用動(dòng)態(tài)創(chuàng)建標(biāo)記
1、不應(yīng)該做什么
從技術(shù)上講,可以把任何內(nèi)容動(dòng)態(tài)添加到網(wǎng)頁(yè)上,但重要的內(nèi)容不要這么做,因?yàn)檫@樣一來(lái),javascript就沒有任何空間去平穩(wěn)退化,如果缺乏javascript的支持,用戶會(huì)永遠(yuǎn)看不到重要的內(nèi)容,而且各大搜索引擎也不支持javascript。
漸進(jìn)增強(qiáng)和平穩(wěn)退化兩項(xiàng)原則要牢記在心,在這里再次總結(jié)一下這兩項(xiàng)重要的原則。
漸進(jìn)增強(qiáng)
應(yīng)該從最核心的部分,也就是從內(nèi)容開始,應(yīng)該根據(jù)內(nèi)容使用標(biāo)記實(shí)現(xiàn)良好的結(jié)構(gòu),然后再逐步加強(qiáng)這些內(nèi)容,這些增強(qiáng)工作既可以是通過(guò)css改進(jìn)呈現(xiàn)效果,也可以是通過(guò)DOM添加各種行為,如果你正在使用DOM添加核心內(nèi)容,那么添加的時(shí)機(jī)未免太遲,核心內(nèi)容應(yīng)在剛開始寫文檔時(shí)就成為文檔的組成部分。
平穩(wěn)退化
漸進(jìn)增強(qiáng)的實(shí)現(xiàn)必然支持平穩(wěn)退化,如果你按照漸進(jìn)增強(qiáng)的原則去充實(shí)內(nèi)容,你為文檔添加的樣式和行為自然就支持平穩(wěn)退化。那些缺乏必要的css和DOM支持的訪問(wèn)者仍可以訪問(wèn)到你的核心內(nèi)容,如果用javascript去添加這些內(nèi)容,它就沒法支持平穩(wěn)退化,不支持javascript就看不到內(nèi)容。
2、內(nèi)容
和往常一樣,任何網(wǎng)頁(yè)都以內(nèi)容為出發(fā)點(diǎn),現(xiàn)在拿下面這段內(nèi)容作為出發(fā)點(diǎn)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>解釋文檔DOM</title>
<style>
body {
font-family:'Helvetica','Arial',sans-serif;
font-size: 10pt;
}
abbr {
text-decoration: none;
border: 0;
font-style: normal;
}
</style>
</head>
<body>
<h1>什么是DOM</h1>
<p><abbr title="萬(wàn)維網(wǎng)聯(lián)盟">W3C</abbr>將<abbr title="文檔對(duì)象模型">DOM</abbr>定義為:</p>
<blockquote cite="http://www.w3.org/DOM/">
<p>一個(gè)平臺(tái)和語(yǔ)言中立的接口,將允許程序和腳本動(dòng)態(tài)訪問(wèn)和更新的內(nèi)容,結(jié)構(gòu)和風(fēng)格的文檔。</p>
</blockquote>
<p>這是一個(gè)<abbr title="應(yīng)用程序設(shè)計(jì)接口">API</abbr>可以用來(lái)瀏覽<abbr title="超文本標(biāo)記語(yǔ)言">HTML</abbr>和<abbr title="可擴(kuò)展標(biāo)記語(yǔ)言">XML</abbr>文檔。</p>
</body>
</html>
<abbr>標(biāo)簽用于縮略語(yǔ)的顯示,默認(rèn)情況會(huì)顯示為帶有下劃線或下劃點(diǎn),但是通過(guò)css的設(shè)置已經(jīng)代替了瀏覽器的默認(rèn)樣式。
<abbr>的title屬性在瀏覽器里是隱藏的,有些瀏覽器會(huì)在你把鼠標(biāo)指針懸停在縮略語(yǔ)上時(shí),將它的title屬性顯示為一個(gè)彈出式的提示消息,這也是瀏覽器所使用的默認(rèn)行為,而不同瀏覽器的默認(rèn)樣式也不盡相同。所以就像剛才用css樣式去代替瀏覽器默認(rèn)樣式那樣,我們也可以用DOM去改變?yōu)g覽器的默認(rèn)行為。
3、顯示縮略語(yǔ)
下面我要做的是將<abbr>標(biāo)簽中的title屬性集中起來(lái)顯示在一個(gè)頁(yè)面,我希望得到的定義列表是這個(gè)樣子:
<dl>
<dt>W3C</dt>
<dd>萬(wàn)維網(wǎng)聯(lián)盟</dd>
<dt>DOM</dt>
<dd>文檔對(duì)象模型</dd>
<dt>API</dt>
<dd>應(yīng)用程序設(shè)計(jì)接口</dd>
<dt>HTML</dt>
<dd>超文本標(biāo)記語(yǔ)言</dd>
<dt>XML</dt>
<dd>可擴(kuò)展標(biāo)記語(yǔ)言</dd>
</dl>
用DOM來(lái)具體實(shí)現(xiàn)這個(gè)定義列表的步驟如下:
遍歷這份文檔中的所有
<abbr>元素。保存每個(gè)
<abbr>元素的title屬性。保存每個(gè)
<abbr>元素包含的文本。創(chuàng)建一個(gè)
<dl>自定義列表元素。遍歷剛才保存的title屬性和
<abbr>元素的文本。創(chuàng)建一個(gè)
<dt>標(biāo)題元素。把
<abbr>元素的文本插入到這個(gè)<dt>元素。創(chuàng)建一個(gè)
<dd>列表項(xiàng)元素。把title屬性插入到這個(gè)
<dd>元素。把
<dt>元素追加到第4步創(chuàng)建的<dl>元素上。把
<dd>元素追加到第4步創(chuàng)建的<dl>元素上。把
<dl>元素追加到explanation.html文檔的body元素上。
按照上面的思路我將編寫一個(gè)displayAbbreviations函數(shù),并存入Enrich_document_content.js中。
function displayAbbreviations(){
if(!document.getElementsByTagName || !document.createElement || !document.createTextNode){
return false;
}
// 取得所有縮略詞
var abbreviations = document.getElementsByTagName('abbr');
if(abbreviations.length < 1) return false;
var defs = new Array();
// 便利這些縮略詞
for(var i = 0;i < abbreviations.length; i++){
var current_abbr = abbreviations[i];
var definition = current_abbr.getAttribute('title');
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
// 創(chuàng)建定義列表
var dlist = document.createElement('dl');
// 遍歷定義
for(key in defs){
var definition = defs[key];
// 創(chuàng)建定義標(biāo)題
var dtitle = document.createElement('dl');
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
// 創(chuàng)建定義描述
var ddesc = document.createElement('dd');
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// 把它們添加到定義列表
dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
// 創(chuàng)建標(biāo)題
var header = document.createElement('h2');
var header_text = document.createTextNode('縮略詞');
header.appendChild(header_text);
// 把標(biāo)題添加到頁(yè)面主體
document.body.appendChild(header);
// 把定義列表添加到頁(yè)面主體
document.body.appendChild(dlist);
}
現(xiàn)在已經(jīng)可以在頁(yè)面顯示出這個(gè)定義列表,不要忘記使用addLoadEvent函數(shù)加載。下面解析一下這個(gè)函數(shù)。
第一步仍然是檢查我用到的DOM方法是否被支持。
第二步是遍歷獲取到的所有<abbr>元素,為了防止html中沒有<abbr>元素,所以在遍歷之前也做了檢查,避免javascript報(bào)錯(cuò)。
第三步用數(shù)組保存<abbr>元素的文本和title屬性,當(dāng)一個(gè)元素中只有一個(gè)子節(jié)點(diǎn),用lastChild獲取其子節(jié)點(diǎn)是一個(gè)好的方法,這里將文本作為數(shù)組的下標(biāo)使用,將字符串作為數(shù)組的下標(biāo)也是一種常用的方式,不要被數(shù)組的下標(biāo)通常為數(shù)字就被禁錮住想法。
第四步用for...in循環(huán),去遍歷之前的數(shù)組,for...in是遍歷用字符串作為下標(biāo)的數(shù)組的首選,將下標(biāo)(key)和下標(biāo)對(duì)應(yīng)的值(value)添加到<dt>和<dl>元素中。
第五步是將這個(gè)定義列表和標(biāo)題插入到body中,這一步?jīng)]什么特別的。
4、displayAbbreviations函數(shù)的兼容性問(wèn)題
按理說(shuō)這個(gè)函數(shù)即檢查了方法、又全部使用的DOM方法,應(yīng)該不存在兼容性問(wèn)題,但問(wèn)題還是有,就是有的瀏覽器不支持<abbr>元素,如果是這樣那么不僅沒有縮略語(yǔ)列表,還會(huì)導(dǎo)致javascript報(bào)錯(cuò)。
出現(xiàn)這個(gè)隱患,如果現(xiàn)在去替換<abbr>元素太過(guò)麻煩,不論何時(shí),要替換html中的元素都不應(yīng)該作為首選。所以保證displayAbbreviations函數(shù)在IE中能夠平穩(wěn)退化沒這個(gè)方案實(shí)現(xiàn)起來(lái)最簡(jiǎn)單,也就是如果瀏覽器不支持<abbr>元素就可以提前退出。
接下來(lái)要解決這個(gè)兼容性問(wèn)題,做到平穩(wěn)退化,我要在第一個(gè)for循環(huán)中加入一個(gè)判斷。if(current_abbr.childNodes.length < 1) continue;這條語(yǔ)句會(huì)讓當(dāng)前元素沒有子節(jié)點(diǎn)的話就進(jìn)入下一次循環(huán),不支持<abbr>元素的瀏覽器在統(tǒng)計(jì)<abbr>元素的子節(jié)點(diǎn)個(gè)數(shù)總會(huì)返回錯(cuò)誤值。
此時(shí)因?yàn)閐efs數(shù)組是空的,所以它將不會(huì)創(chuàng)建出任何<dt>和<dd>元素,在for...in循環(huán)之后,寫一個(gè)函數(shù)出口,if(dlist.childNodes.length < 1) return false;如果dlist沒有子節(jié)點(diǎn),那么直接跳出函數(shù),這樣就避免了報(bào)錯(cuò)的可能性。以下是改進(jìn)后的代碼
function displayAbbreviations(){
if(!document.getElementsByTagName || !document.createElement || !document.createTextNode){
return false;
}
// 取得所有縮略詞
var abbreviations = document.getElementsByTagName('abbr');
if(abbreviations.length < 1) return false;
var defs = new Array();
// 便利這些縮略詞
for(var i = 0;i < abbreviations.length; i++){
var current_abbr = abbreviations[i];
if(current_abbr.childNodes.length < 1) continue;
var definition = current_abbr.getAttribute('title');
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
// 創(chuàng)建定義列表
var dlist = document.createElement('dl');
// 遍歷定義
for(key in defs){
var definition = defs[key];
// 創(chuàng)建定義標(biāo)題
var dtitle = document.createElement('dl');
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
// 創(chuàng)建定義描述
var ddesc = document.createElement('dd');
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// 把它們添加到定義列表
dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
if(dlist.childNodes.length < 1) return false;
// 創(chuàng)建標(biāo)題
var header = document.createElement('h2');
var header_text = document.createTextNode('縮略詞');
header.appendChild(header_text);
// 把標(biāo)題添加到頁(yè)面主體
document.body.appendChild(header);
// 把定義列表添加到頁(yè)面主體
document.body.appendChild(dlist);
}
如果瀏覽器不支持<abbr>標(biāo)簽,也不會(huì)出任何錯(cuò)誤,但是也會(huì)看不到縮略語(yǔ)列表,不過(guò)縮略語(yǔ)列表也算不上頁(yè)面必不可少的組成部分,如果真的是必不可少的內(nèi)容,從一開始就應(yīng)該把它包括在標(biāo)記里。
5、顯示文獻(xiàn)來(lái)源鏈接表
在這個(gè)案例中,還有另一個(gè)增強(qiáng)文檔的例子,先來(lái)看看html中這段標(biāo)記。
<blockquote cite="http://www.w3.org/DOM/">
<p>一個(gè)平臺(tái)和語(yǔ)言中立的接口,將允許程序和腳本動(dòng)態(tài)訪問(wèn)和更新的內(nèi)容,結(jié)構(gòu)和風(fēng)格的文檔。</p>
</blockquote>
<blockquote>元素包含一個(gè)cite屬性,它可以是一個(gè)URL地址,告訴人們<blockquote>元素的內(nèi)容引自哪里,從理論上講這是一個(gè)文獻(xiàn)資料與縣官網(wǎng)頁(yè)鏈接起來(lái)的好辦法。但實(shí)際上瀏覽器會(huì)完全忽略cite屬性,所以我要把這些信息收集起來(lái),以一種更有意義的方式把它們顯示在網(wǎng)頁(yè)上。
按照以下步驟完成displayCitetions函數(shù),并存入Enrich_document_content.js文件中。
遍歷這個(gè)文檔里所有
<blockquote>元素。從
<blockquote>元素提取出cite屬性的值。創(chuàng)建一個(gè)標(biāo)識(shí)文本是source的鏈接。
把這個(gè)鏈接賦值為
<blockquote>元素的cite屬性值。把這個(gè)鏈接插入到文獻(xiàn)節(jié)選的末尾。
function displayCitetions(){
if(!document.getElementsByTagName || !document.createElement || !document.createTextNode){
return false;
}
// 取得所有引用
var quotes = document.getElementsByTagName('blockquote');
// 遍歷引用
for(var i = 0;i < quotes.length;i++){
// 如果沒有cite屬性,繼續(xù)循環(huán)
if(!quotes[i].getAttribute('cite')) continue;
// 保存cite屬性
var url = quotes[i].getAttribute('cite');
// 取得引用中的所有元素節(jié)點(diǎn)
var quoteChildren = quotes[i].getElementsByTagName('*');
// 如果沒有元素節(jié)點(diǎn)進(jìn)入下一次循環(huán)
if(quoteChildren.length < 1) continue;
// 取得引用中的最后一個(gè)元素節(jié)點(diǎn)
var elem = quoteChildren[quoteChildren.length - 1];
// 創(chuàng)建標(biāo)記
var link = document.createElement('a');
var link_text = document.createTextNode('source');
link.appendChild(link_text);
link.setAttribute('href',url);
var superscript = document.createElement('sup');
superscript.appendChild(link);
// 把標(biāo)記添加到引用中的最后一個(gè)元素節(jié)點(diǎn)
elem.appendChild(superscript)
}
}
函數(shù)執(zhí)行完畢后這個(gè)<blockquote>元素的引用將會(huì)添加到段落最后的source上標(biāo)中。下面來(lái)解析一下這個(gè)函數(shù)。
第一部分是篩選出cite屬性,再去獲取<blockquote>元素的最后一個(gè)子節(jié)點(diǎn),這里<blockquote>的lastChild有可能是個(gè)換行符,所以在函數(shù)中是獲取的<blockquote>下的所有元素對(duì)象,這樣就可以方便的獲取<blockquote>的最后一個(gè)子元素。
第二部分是創(chuàng)建鏈接和插入鏈接,最終讓<sup>元素包含<a>元素,而<a>元素href屬性保存了cite的屬性值,也就是一個(gè)url地址。最后將<sup>元素插入到<blockquote>元素的最后。