DOM技巧篇(DOM基礎知識)
講到這里,我們就要了解一下DOM的一些基礎知識了。
DOM(Document Object Model 文檔對象模型)是HTML和XML的應用程序接口(API)。DOM將把整個頁面規(guī)劃成有節(jié)點層級構成的文檔。HTML或XML頁面的每一個部分都是一個節(jié)點的衍生物。
光說可能還不怎么好理解,那么來看看我們ajax標簽導航的DOM結構吧,如圖五(截取的FIXFOX中的DOM圖象):
DOM通過創(chuàng)建樹來表示文檔,從而使開發(fā)著對文檔的內(nèi)容和結構具有空前的控制力。用DOM API可以輕松地刪除,添加和替換節(jié)點。
簡單的了解了什么是DOM后(要想了解更多的Javascript DOM 編程知識,推薦大家看看《Javascript DOM 編程藝術》和《Javascript高級編程》這兩本書,還有到作者的網(wǎng)站去看看,也可以直接到W3C去查詢相關信息。),來看看我們這個程序里需要用到的DOM知識吧:document.getElementsByTagName()還有之前提到的$(i)函數(shù),它們都是做什么用的呢?
document.getElementById("DOMId"):它返回的是以ID為表示的節(jié)點,而大家都知道id在頁面中是唯一的,所以getElementById是在頁面中搜索DOM節(jié)點最直接的方法,很常用。不過它只能查尋單個的DOM節(jié)點,我們要查詢一組怎么辦呢? 我們可以使用getElementsByTagName和getElementsByName。
從方法名字中的“Elements”我們也可以知道,這兩個方法返回的是一組元素(數(shù)組)
getElementsByTagName:(核心[XML]DOM)用來返回一個包含所有tagName(標簽名)特性等于某個指定值的元素的NodeList。
getElementsByName:(HTML DOM)用來獲取所有name特性等于指定值的元素。但是這個方法在IE6和opera7.5中支持不是很好,會有錯誤產(chǎn)生,所以(個人)建議一般不要用。
OK,在對使用DOM來查尋節(jié)點的知識有了了解后,我們再來看這段代碼:
/* =========================================================== * 函數(shù)名稱:$(i) * 參數(shù)說明:i - 目標節(jié)點名稱 * 函數(shù)功能:獲取指定的目標DOM節(jié)點 * 返 回 值:返回要搜索的目標DOM節(jié)點 * 使用方法:$("frmSearch") ============================================================ */ function $(i){ if(!document.getElementById)return false; if(typeof i==="string"){ if(document.getElementById && document.getElementById(i)) { // W3C DOM return document.getElementById(i); } else if (document.all && document.all(i)) { // MSIE 4 DOM return document.all(i); } else if (document.layers && document.layers[i]) { // NN 4 DOM.. note: this won't find nested layers return document.layers[i]; } else { return false; } } else{return i;} }
這個函數(shù)主要是來查找指定的DOM節(jié)點的,主要是通過document.getElementById()方法,但是我們又看到了document.all()和document.layers[]方法,這個就是瀏覽器大戰(zhàn)時期(各個瀏覽器對DOM標準支持不程度不同),各大瀏覽器提供商制定的各自的DOM支持規(guī)范而造成的,我們的CSS HACKS其實也是由于這個原因才會出現(xiàn)的。扯遠了,document.all()是IE瀏覽器(IE5以上版本)中特有的查詢節(jié)點的方法,而document.layers[i]則是其他瀏覽器(主要是NetScape的)的瀏覽器特有的。我們通過$(i)函數(shù)來統(tǒng)一調(diào)用,從而解決了瀏覽器兼容的問題。
而下面這里的代碼:
// DOM節(jié)點(tabs)不存在或者瀏覽器不支持getElementsByTagName()函數(shù)不執(zhí)行 if(!tabs || !document.getElementsByTagName) return false;
這里if(!tabs || !document.getElementsByTagName) return false;這么寫是很有必要的,這里是一種預留退路的思想(在《Javascript DOM 編程藝術》一書一直灌輸?shù)乃枷耄@么寫在不支持getElementsByTagName()方法時我們的函數(shù)就不會執(zhí)行,在不tabs($("news")和$("sports")),不存在的時候(可能是我們把參數(shù)名寫錯了),函數(shù)也不執(zhí)行了,從而避免了彈出或顯示腳本錯誤的信息。下面我們還將看到這一思想的體現(xiàn),不過我們還是先來看看緊接的代碼:
var theList = tabs.getElementsByTagName("li"); // 搜尋導航標簽(ID為tabs)里的所有l(wèi)i標簽 var theLink = tabs.getElementsByTagName("a"); // 搜尋導航標簽(ID為tabs)里的所有a標簽
為什么要找出所有的li和a標簽呢?呵呵,這個是由于我這里采取的設置樣式的結構決定的(其實我也老是覺得不怎么好,不過這個思維個人比較好理解--頭疼醫(yī)頭,腳疼醫(yī)腳的方法)?纯次覀兩线叺腃SS樣式,大家看到了,我們是用改變標簽菜單(li)的樣式來實現(xiàn)特別顯示當前標簽的。那么我這里就自然要獲取所有的li標簽,然后給他添加onclick來調(diào)用ajaxInject(ListName,tabId,tarObj,URL),從而改變標簽的樣式。看看我來實現(xiàn)這個功能的代碼吧:
for(var j=0;j<theList.length;j++){ var theTab = theList[j]; if(theTab.parentNode!=tabs) continue; var theA = theLink[j]; // 屏蔽掉a標簽默認的處理(打開新鏈接)事件 theA.onclick = function(){ return false; } // 為導航TAB菜單(li)設置onclick處理方法(函數(shù)) theTab.onclick = function(){ var theClass = this.className; if(theClass!="current" && theClass!="first"){ var objId = this.getAttribute("id").split("-")[1]; // 當前選中標簽(li)在菜單(ul)中的索引值 var tarObj = this.getAttribute("id").split("-")[0]; // 要顯示信息的目標DOM節(jié)點ID值 var theURL = tarObj + "/" + tarObj + objId + ".htm"; // 要異步加載的URL地址 ajaxInject($(tarObj),objId,tarObj,theURL); return false; } } }
我們通過使用一個for循環(huán)來遍歷所有的li和a標簽(由于它們個數(shù)相同,所以索引的值相同),然后分別為它們設置onclick事件。先看我們給a標簽添加的事件的代碼:
var theA = theLink[j]; // 屏蔽掉a標簽默認的處理(打開新鏈接)事件 theA.onclick = function(){ return false; }
一個簡單的return false,可別小看它哦,有幾個作用哦,首先a標簽是嵌套在li標簽里的,我們點li時就一定會出發(fā)a標簽的默認行為,打開鏈接的頁面。而我們ajax標簽導航就是希望不刷(打開)新頁面,在指定的DOM節(jié)點顯示信息。當然就不能讓a標簽的默認行為啟動了,而簡單的一個return false;就解決了這個問題。哦,這里還有個問題就是DOM事件的冒泡的順序,詳細的介紹大家可以在《Javascript高級編程》一書中查到。
那有些朋友會問,為什么要在里面加個a標簽呢?反正你是改變的是li的樣式,點了li,改變li的樣式,然后刷新指定DOM節(jié)點的信息不就完成了ajax標簽導航的功能(效果)了嗎?
是啊,不過我在這里要提的就是剛才提到的一個預留退路的思想,如果想上面說的那樣做了,當然是沒有什么問題,但前提是用戶的瀏覽器支持javascript腳本,或者說用戶打開了執(zhí)行腳本的權限。一旦用戶的瀏覽器不支持javascript或者出于安全原因關閉了腳本執(zhí)行功能。這個時候,當用戶點li時是沒有任何反映的。而我這里的處理就考慮到了當javascript執(zhí)行不了的情況,這時候,用戶點鏈接就可以打開我們原本要用ajax加載的內(nèi)容了。
其實這里還有各個比較簡單的方法來達成我提到的相同的效果,就是對li使用onmouseover事件,想想為什么?因為只要鼠標劃過時,就觸發(fā)了ajaxInject($(tarObj),objId,tarObj,theURL);改變了標簽的樣式,刷新了內(nèi)容。當點擊鏈接時,就可以彈出頁面了,F(xiàn)在網(wǎng)易和雅虎中國就是這么處理的。其實我這么做主要是處于個人習慣,比較喜歡用onclick,還有就是這里有個分隔線的效果,看上去比網(wǎng)易的只用一個背景圖片酷,當然我的效果是學的雅虎(不是雅虎中國)的。不過雅虎的標簽樣式的處理方式要比我現(xiàn)在的更巧妙,下次有時間再跟大家分析下雅虎的標簽導航效果。
又扯遠了,OK,接下來我們就是要改變樣式和ajax刷新內(nèi)容了。不過在這個之前,看看我做了什么準備。
<ul class="tabs" id="news"> <li class="first" id="news-0"><a href="news/news0.htm">網(wǎng)站重構</a><span></span></li> <li id="news-1"><a href="news/news1.htm">CSS布局實錄</a><span></span></li> <li id="news-2"><a href="news/news2.htm">海嘯的地盤</a><span></span></li> <li id="news-3"><a href="news/news3.htm">Ajax高級編程</a><span></span></li> </ul>
var objId = this.getAttribute("id").split("-")[1]; // 當前選中標簽(li)在菜單(ul)中的索引值 var tarObj = this.getAttribute("id").split("-")[0]; // 要顯示信息的目標DOM節(jié)點ID值 var theURL = tarObj + "/" + tarObj + objId + ".htm"; // 要異步加載的URL地址
看看我文章前面部分羅列的東西,現(xiàn)在就在這里有了回應了
id="news" - news就是我們的導航標簽的ID; id="news-0" - news-0 通過”-“分開,我們就分別可以得到news(導航標簽ID),0(標簽[li]在導航標簽中的索引值)
好現(xiàn)在就要改變標簽的樣式了
/* =========================================================== * 函數(shù)名稱:ajaxInject(ListName,tabId,tarObj,URL) * 參數(shù)說明:ListName - 標簽菜單DOM節(jié)點ID * tabId - 選中的標簽(在ListName中的)索引值 * tarObj - 要顯示返回信息的目標DOM節(jié)點ID值 * URL - 要異步處理的URL地址 * 函數(shù)功能:設置當前選中標簽(li)的樣式, * 將返回信息寫到指定DOM節(jié)點中。 * 返 回 值:無 * 使用方法:ajaxInject($(tarObj),objId,tarObj,theURL); ============================================================ */ function ajaxInject(ListName,tabId,tarObj,URL){ if(!ListName || !document.getElementsByTagName) return false; var Tabs = ListName; var theLi = Tabs.getElementsByTagName("li"); for(var i=0;i<theLi.length;i++){ // 設置當前選中標簽的樣式 if(i==tabId){ if(i==0){ theLi[tabId].className = "first"; // 當選中第一項的樣式 } else{// theLi[tabId].className = "current"; // 選中其他項的樣式 } var msgBox = tarObj+"Cnt"; var loadstatustext="<div class='loading'><img src='img/loading.gif' alt='正在加載內(nèi)容, 請稍候...' />正在加載內(nèi)容, 請稍候...</div>"; $(msgBox).innerHTML = loadstatustext; // 加載信息時的提示信息 var para = "?d=" + Math.random(); // URL后的參數(shù),接Math.random()(一個隨機數(shù)),目的是處理ajax的緩存問題 var myAjax = ajaxUpdater(msgBox,"get",URL,para); } else{// 設置其他標簽的樣式 theLi[i].className = ""; if(tabId!=0){ theLi[tabId-1].className = "off"; // 當不是第一項時,隱藏選中項的前一項的分隔標簽 } } } }
這里又跟前面的
id="newsCnt" - newsCnt就是我們要寫入信息的目標DOM節(jié)點; class="first" - first當前(第一個)標簽的樣式;
對應起來了。
for(var i=0;i<theLi.length;i++){ // 設置當前選中標簽的樣式 if(i==tabId){ if(i==0){ theLi[tabId].className = "first"; // 當選中第一項的樣式 } else{ theLi[tabId].className = "current"; // 選中其他項的樣式 } } else{// 設置其他標簽的樣式 theLi[i].className = ""; if(tabId!=0){ theLi[tabId-1].className = "off"; // 當不是第一項時,隱藏選中項的前一項的分隔標簽 } } }
上面這段代碼就是具體改變樣式的,i==tabId比較當前標簽的索引值(作用就是確認是否是選中的標簽),相等了就給標簽設置樣式了。i==0表明第一項被選種(由于我的第一項的背景特殊的)給它加上“first”樣式,其余項被選中則加上“current”樣式。
接這就是處理分隔錢的樣式了,跟設置背景大同小意,這里要說的是我們在寫CSS的時候要把li(選中和失去焦點)的樣式設置好。還是我之前提到的,YAHOO的做得很好,我記得網(wǎng)上也有關于滑動門技術CSS寫法的介紹,大家可以看看是怎么來設置樣式的,還有這里給大家推薦個小軟件《CSS Tab Designer 2》。
恩,現(xiàn)在要刷新指定DOM節(jié)點的內(nèi)容了,用一個簡單的var myAjax = ajaxUpdater(msgBox,"get",URL,para);就解決問題了。不過我們看看在把信息寫到指定DOM節(jié)點前,我做了什么:
var msgBox = tarObj+"Cnt"; var loadstatustext="<div class='loading'><img src='img/loading.gif' alt='正在加載內(nèi)容, 請稍候...' />正在加載內(nèi)容, 請稍候...</div>"; $(msgBox).innerHTML = loadstatustext; // 加載信息時的提示信息
這里做了一個提示的處理,因為ajax是異步的加載,在獲取很長(信息量很大)的內(nèi)容時,會有一個延時,如果在這個期間不給任何提示信息的話,我們要刷新的DOM節(jié)點會出現(xiàn)白白的一片,這個當然是不美觀的。所以在這之前,我們給用戶一個提示信息,說明正在加載信息會顯得更人性化些。當然,大家知道的網(wǎng)易的處理會更好些,做一個延遲的window.setTimeout效果。
出處:藍色理想
責任編輯:moby
上一頁 Javascript技巧篇 下一頁 大結局
◎進入論壇網(wǎng)頁制作、WEB標準化版塊參加討論,我還想發(fā)表評論。
|