勤于思考是每位有創(chuàng)新精神的網(wǎng)頁(yè)設(shè)計(jì)人員都應(yīng)該具備的特質(zhì)第美。
平穩(wěn)退化
第一個(gè)問(wèn)題增显,如果JS功能被禁用易稠,會(huì)怎么樣拿霉?把href屬性設(shè)置為一個(gè)真實(shí)存在的值吟秩,能夠讓之前提到的圖片庫(kù)平穩(wěn)退化。
JS與HTML標(biāo)記是分離的嗎绽淘?
第二個(gè)問(wèn)題涵防,網(wǎng)頁(yè)的行為層(JS)是作用于其結(jié)構(gòu)層(HTML)之上的么?還是兩種代碼混雜在一起收恢?
- 必須找到一種“掛鉤”把JS代碼與HTML文檔中的有關(guān)標(biāo)記關(guān)聯(lián)起來(lái)武学。在頁(yè)面元素上設(shè)置class和id是常見(jiàn)的方式,應(yīng)該合理設(shè)置這些類型伦意,少為佳火窒。
添加事件處理函數(shù)
現(xiàn)在我需要編寫(xiě)一個(gè)簡(jiǎn)短的函數(shù)把有關(guān)操作關(guān)聯(lián)到onclick事件上,函數(shù)命名為prepareGallery
- 檢查當(dāng)前瀏覽器是否理解getElementsByTagName
- 檢查當(dāng)前瀏覽器是否理解getElementById
- 檢查當(dāng)前網(wǎng)頁(yè)是否存在一個(gè)id為imagegallery的元素
- 遍歷imagegallery元素中的所有鏈接
- 設(shè)置onclick事件驮肉,讓它在有關(guān)鏈接被點(diǎn)擊時(shí)完成以下操作熏矿。
- 把這個(gè)鏈接作為參數(shù)傳遞給showPic函數(shù)
- 取消鏈接被點(diǎn)擊時(shí)的默認(rèn)行為,不讓瀏覽器打開(kāi)這個(gè)鏈接
檢查點(diǎn)
var supported=document.getElementsByTagName&&document.getElementById;
if(!supported)return;
//更加針對(duì)的檢查
if(!document.getElementById("imagegallery")){
return false;
}
作為一條原則离钝,如果想用JS給某個(gè)網(wǎng)頁(yè)添加一些行為票编,就不應(yīng)該讓JS代碼對(duì)這個(gè)網(wǎng)頁(yè)結(jié)構(gòu)有任何依賴。
整理后的代碼如下:
function prepareGallery(){
if(!document.getElementsByTagName) return false;
if(!document.getElementById)return false;
if(!document.getElementById("imagegallery"))return false;
}
變量名里有什么
使用變量名進(jìn)行對(duì)象的臨時(shí)存儲(chǔ)卵渴。
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");
}
遍歷
改變行為
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(){
showPic(this);
return false;
}
}
}
共享onload事件
讓函數(shù)在網(wǎng)頁(yè)加載完畢后立即執(zhí)行慧域。
PS:但是如果存在多個(gè)函數(shù),需要在網(wǎng)頁(yè)加載完畢之后執(zhí)行浪读,可以先創(chuàng)建一個(gè)匿名函數(shù)來(lái)容納這兩個(gè)函數(shù)昔榴,然后把這兩個(gè)匿名函數(shù)綁定到onload事件上。
window.onload=function(){
firstFunction();
secondFunction();
}
這里還有一個(gè)彈性最佳的解決方案碘橘。使用addLoadEvent互订,它只有一個(gè)參數(shù),打算在頁(yè)面加載完畢時(shí)執(zhí)行函數(shù)的名字痘拆。
addLoadEvent函數(shù)將要完成的操作仰禽。
- 把現(xiàn)有的window.onload事件處理函數(shù)的值存入變量oldonload
- 如果在這個(gè)處理函數(shù)上海沒(méi)有綁定任何函數(shù),就像平時(shí)那樣把新函數(shù)添加給它纺蛆。
- 如果在這個(gè)處理函數(shù)上已經(jīng)綁定了一些函數(shù)吐葵,就把新函數(shù)追加到現(xiàn)有指令的末尾。
addLoadEvent函數(shù)的代碼清單桥氏。
function addLoadEvent(func){
var oldonload=window.onload;
if(typeof window.onload!='function'){
window.onload=func;
}else{
window.onload=function(){
oldonload();
func();
}
}
}
//上面的代碼我們就可以這樣實(shí)現(xiàn)折联。
addLoadEvent(firstFunction);
addLoadEvent(secondFunction);
不要做太多的假設(shè)
在代碼里用到了id屬性值等于palceholder和description的元素,但我并未對(duì)這些元素是否存在做任何檢查识颊。我們需要增加一些語(yǔ)句來(lái)檢查這些元素是否存在。
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;
}
改進(jìn)后的showPic函數(shù)不再假設(shè)有關(guān)標(biāo)記文檔里肯定存在著placeholder圖片和description元素,即使沒(méi)有placeholder圖片祥款,也不會(huì)發(fā)生任何JS錯(cuò)誤清笨。但是有一個(gè)問(wèn)題就是如果把placeholder圖片從標(biāo)記文檔里刪掉并在瀏覽器里刷新這個(gè)頁(yè)面,就會(huì)出現(xiàn)無(wú)論點(diǎn)擊清單里的哪一個(gè)鏈接刃跛,都沒(méi)有任何響應(yīng)抠艾。
原因在于showPic函數(shù)肯定會(huì)正常返回,基于這一假設(shè)桨昙,prepareGallery函數(shù)取消了onclick事件的默認(rèn)行為检号。
links[i].onclick=function(){
showPic(this);
return false;
}
是否要返回一個(gè)false值來(lái)取消onclick事件的默認(rèn)行為,應(yīng)該由showPic函數(shù)決定蛙酪。showPic應(yīng)返回兩個(gè)可能的值齐苛。
- 如果圖片切換成功,返回true桂塞。
- 如果圖片切換不成功凹蜂,返回false。
源碼鏈接:
優(yōu)化
盡管代碼已經(jīng)相當(dāng)完善了阁危,在showPic函數(shù)里仍存在一些需要處理的假設(shè)玛痊。
- 檢查title屬性是否真的存在
if(whichpic.getAttribute("title")!=null)
或者
if(whichpic.getAttribute("title"))
代碼優(yōu)化如下:
var text=whichpic.getAttribute("tilte")?whichpic.getAttribute("title"):"";
- 檢查placeholder元素是否存在
if(placeholder.nodeName!="IMG")return false;
請(qǐng)注意,nodeName屬性總是返回一個(gè)大寫(xiě)字母的值狂打,即使元素在HTML文檔中是小寫(xiě)字母擂煞。
-對(duì)description元素的第一個(gè)子元素是一個(gè)文本節(jié)點(diǎn)進(jìn)行檢查。
if(description.firstChild.nodeType==3){
description.firstChild.nodeValue=text;
}
優(yōu)化后的代碼如下:
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;
}
PS:在實(shí)際工作中趴乡,你要自己決定是否真的需要這些檢查对省。它們針對(duì)的是HTML文檔有可能不在你控制范圍內(nèi)的情況。
鍵盤訪問(wèn)
在有關(guān)onclick事件處理的腳本中浙宜,有一項(xiàng)優(yōu)化工作是不能不考慮的官辽。
公所周知,不使用鼠標(biāo)也可以瀏覽Web粟瞬。鍵盤上的Tab鍵可以讓我們從這個(gè)鏈接移動(dòng)到另一個(gè)鏈接同仆,而按下回車鍵將啟用當(dāng)前鏈接。有個(gè)名叫onkeypress的事件處理函數(shù)是專門用來(lái)處理鍵盤事件的裙品。按下鍵盤上任何一個(gè)按鍵都會(huì)觸發(fā)onkeypress事件俗批。
一種簡(jiǎn)單的方法:
可以確保onkeypress模仿onclick事件行為。
links[i].onkeypress=links[i].onclick;
小心onkeypress
最后的決定是不添加onkeypress事件處理函數(shù)市怎。原因是這個(gè)事件處理函數(shù)很容易出問(wèn)題岁忘。用戶每按下一個(gè)案件都會(huì)觸發(fā)它。
onclick事件與鼠標(biāo)點(diǎn)擊動(dòng)作相關(guān)聯(lián)的感覺(jué)区匠。事實(shí)卻并非如此干像。在幾乎所有瀏覽器里帅腌,用tab鍵移動(dòng)到某個(gè)鏈接然后按下回車鍵的動(dòng)作也會(huì)觸發(fā)onclick事件。從這點(diǎn)看來(lái)麻汰,把它命名為onactivate更恰當(dāng)速客。
最好不要使用onkeypress事件處理函數(shù)。onclick事件處理函數(shù)已經(jīng)能滿足需要五鲫,雖然它叫做onclick溺职,但是它對(duì)鍵盤的訪問(wèn)支持相當(dāng)完美。
最終代碼:
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;
}
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);
}
}
}
把JS與CSS結(jié)合起來(lái)
把內(nèi)嵌型事件處理函數(shù)移出標(biāo)記文檔時(shí)位喂,我在文檔里為JS代碼留下了一個(gè)“掛鉤”浪耘,如<ul id="imagegallery">,這個(gè)掛鉤完全可以用在CSS樣式表里塑崖。
DOM Core和HTML-DOM
在編寫(xiě)JS代碼時(shí)只用到以下幾個(gè)DOM方法七冲。
- getElementById
- getElementByTagName
- getAttribute
- setAttribute
這些方法都是DOM Core的組成部分。它們并不專屬于JS弃舒,支持DOM的任何一種程序設(shè)計(jì)語(yǔ)言都可以使用它們癞埠。它們的用途也并非僅限于處理網(wǎng)頁(yè),它們可以用來(lái)處理用任何一種標(biāo)記語(yǔ)言編寫(xiě)出來(lái)的文檔聋呢。
PS:在Web文檔中苗踪,同樣的代碼即可以使用DOM來(lái)編寫(xiě),也可以使用HTML-DOM來(lái)編寫(xiě)削锰。通常情況下通铲,HTML-DOM的語(yǔ)句更簡(jiǎn)短。如:var source=whichpic.getAttribute("href");可以使用HTML-DOM達(dá)到同樣目的的語(yǔ)句:var source=whichpic.href;
結(jié)構(gòu)與行為的分離程度越大越好器贩。