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