web關聯菜單實現方法

來源:互聯網
上載者:User
web|菜單

  筆者日前涉及一個大型ASP項目的開發,其中多次遇到多維下拉式功能表(對於WEB項目而言,這裡專指網頁中的<SELECT>元素)的問題,菜單中的資料均需要從資料庫中取出,並動態產生和變化。筆者以前曾發表過如何利用PHP和JavaScript製作二維下拉式功能表的文章,目前這類文章在網路上也頗為多見,思路也有很多創新,但對於本文中談到的多維下拉式功能表,很少有人談及。筆者無意班門弄斧,只是想把開發中的一點經驗和技巧總結出來,希望能給廣大的讀者一點啟示。

  多維下拉式功能表,顧名思義,也就是根據一個下拉式功能表的選擇,來控制其它一個或多個下拉式功能表中顯示的資料。舉個例子來說明,在一個WEB管理系統中,使用者要求通過選擇單位名稱,進而選擇部門名稱,最後選擇員工。也就是說,需要提供三個下拉式清單,每個下拉式清單之間需要建立關聯。通過第一個能選擇第二個,並同時選擇第三個,第四個等等。那麼每一個下拉式清單的顯示資料之間如何建立關聯,關聯起來的資料又如何通過事件驅動,這正是本文所要討論的主要內容。

  熟悉VB、Delphi等RAD開發工具的朋友可能會感到疑惑。的確,在這些所謂的RAD開發工具中,我們可以利用Combo Boxes控制項很容易的實現下拉式功能表,進而實現他們之間的關聯。但是由於WEB項目中的下拉式功能表是利用HTML中的<SELECT>元素來實現的,而由於HTML的局限性,無論是對象的屬性還是事件模型,都遠沒有RAD工具那麼強大。所以,開發WEB項目,這種問題只能有一個解決途徑,那就是利用JavaScript(也可能有人說可以利用java來實現,那我也只好說,您是高手)。
關於JavaScript的使用,不是本文的重點,所以不瞭解或不熟悉JavaScript的讀者請先參考JavaScript的相關資料以擷取相關資訊。

  好,言歸正傳,下面我們就一起來探討多維下拉式功能表的設計問題。為了討論的方便,我們就以上文提到的三維下拉式功能表為例,向大家一步一步的講述設計的思路。

  一、分析菜單的運作流程

  首先,使用者會選擇單位列表,並從中選出一個單位名稱,我們假定為單位A。這時,另外兩個下拉式清單應該做些什嗎?對,我們希望第二個下拉式功能表能立即反映第一個下拉式功能表的選擇,顯示並僅顯示單位A中的所有部門,我們再假定菜單中第一項為部門A(預設的顯示項目)。那麼可能有讀者會問,第三個下拉式功能表不是應該同時選擇與部門A對應的員工資料嗎?這是個很好的想法,是的,我們也應該立即改變第三個下拉式功能表中的資料為部門A中的員工列表。同樣,當使用者選擇部門時,又會改變員工列表。依次類推。
以上是一種思路,可能有一些特殊的情況,例如,在改變部門列表時,並不希望立即就選擇一個部門,而是顯示一個"請選擇"字樣的提示條目。
思路已經有了,下面就是如何?的問題了。

  二、菜單資料的容器

  根據常規想法,當使用者從第一個下拉式功能表中選擇單位名稱,我們可以從資料庫中選出與之相關的部門資料,並顯示出來,這似乎也不無可行。但有經驗的開發人員就會發現,由於Web頁面的無狀態性,當你再次串連資料庫時,Web頁面必須得再次重新整理。這是一個頭疼的問題,一方面我們想串連資料庫,可另一方面我們必須得保持使用者已輸入資料不被破壞。即使如此,估計使用者也並不希望看到一個每次選擇都重新整理一次的局面。難道就沒有更好的辦法?

  有,那就是利用JavaScript的多維陣列。我們為什麼不可以把需要顯示的資料在第一次串連資料庫時全取出來,放到數組中去?這樣在每次改變菜單資料時,只要從數組中取得資料,不就可以大大的提高效率了嗎?這是個令人振奮的方法,這個方法中提到的JavaScript數組,我們暫且稱之為菜單資料的容器。

  您的思路是不是一下子豁然開朗?可是躍躍欲試一番以後,是不是感到事情好像並不是那麼簡單?問題又來了,容器的結構該如何設計,資料之間的關聯又如何?呢?別急,其實這正是問題之所在。

  三、資料容器結構的設計

  說起容器結構的設計,我們得感謝資料結構中的鏈表給我們的啟示--鏈表是通過指標聯絡在一起的。雖然JavaScript中沒有指標的概念,但我們為什麼不可以類比一下。

  為了討論方便,我們假定資料庫的結構如下:

1、 單位資訊表:(unit_id, unit_name, …)
2、 部門資訊表:(dept_id, unit_id, dept_name, …)
3、 員工資訊表:(emp_id, dept_id, emp_name, …)

  利用這個資料庫結構,我們可以很容易的推匯出數組的結構。您說的沒錯,這應該是一個多維陣列。其定義方法應該象下面這樣(以部門為例):

var arrDept = new Array();
arrDept[0] = new Array(unit_id0, dept_id0 dept_name0);
arrDept[1] = new Array(unit_id1, dept_id1, dept_name1);

arrDept[n] = new Array(unit_idn, dept_idn, dept_namen);

  n的大小視實際資料量而定,例如在單位下拉式功能表中,n代表單位的總數。但讀者必須明白,正是由於n的不確定性,以上的代碼必須通過程式動態產生。例如對於ASP程式,我們可以在<script></script>之間嵌入這樣的一段代碼:

<%
Dim rs, i
'[串連資料庫,取出資料]
response.write "var arrDept = new Array();" & vbNewLine
i = 0
while not rs.EOF
response.write "arrDept[" & i & "] = new Array('" & rs(unit_id) & "', '" & _
rs(dept_id) & "', '" & rs(dept_name) & "');" & vbNewLine
rs.MoveNext
i = i +1
wend

%>

代碼拷貝框

<%Dim rs, i'[串連資料庫,取出資料]response.write "var arrDept = new Array();" & vbNewLinei = 0while not rs.EOFresponse.write "arrDept[" & i & "] = new Array('" & rs(unit_id) & "', '" & _rs(dept_id) & "', '" & rs(dept_name) & "');" & vbNewLiners.MoveNexti = i +1wend…%>

[Ctrl+A 全部選擇 然後拷貝]

  以上這段代碼用來從部門表中取出資料,併產生相關的JavaScript多維陣列。這隻是筆者的一種示範,讀者完全可以使用更靈活的方法來提取資料。

  說來說去,我們還是要回到JavaScript數組的結構定義上來。聰明的讀者應該已經從上述的代碼中發現了數組的定義方法,但筆者還是要不厭其煩的再補充一遍:

  我們把數組的第一個元素定義為指標,用來指向其"父結點"。等等,什麼是父結點?父結點說明白了就是上一級結點,例如,部門的上一級是單位,員工的上一級是部門。那麼第二個元素是什嗎?讓我們來看一下下面的一段<SELECT>定義:

<SELECT NAME="s1" >
<OPTION Value="1">單位1</OPTION>
<OPTION Value="2">單位2</OPTION>
….
</SELECT>

  <OPTION>元素的Value屬性從哪裡來呢?對,就是第二個元素,依此類推,第三個元素指的就是顯示在菜單中的資料嘍,即上面的"單位1"、"單位2"…

  讀者到這裡可能有些糊塗了,說這麼多,這個數組到底是什麼樣?別急,讓我們以部門為例,給出一段根據部門庫中的資料動態產生的數組類比代碼:

<SCRIPT LANGUAGE="JAVASCRIPT">
<!-

var arrDept = new Array();
arrDept[0] = new Array('u01', 'd01', '部門1');
arrDept[1] = new Array('u01', 'd02', '部門2');

arrDept[8] = new Array('u06', 'd08', '部門8');

arrDept[15] = new Array('u08', 'd15', '部門15');

->
</SCRIPT>

  數組終於真相大白。以"u"開頭資料的代表單位的編號,即,指向單位的指標,也就是說,我們可以通過這個編號來確定該單位所屬的部門;以"d"開頭的資料代表部門的編號,用來供下一級選單(即員工選單)的指標使用。(註:實際使用中,資料格式根據情況而定)
有一個問題,象單位這樣沒有父結點的數組該如何定義?很簡單,把數組的第一個元素全部置為0就行了。

  下一步,是到我們編寫JavaScript代碼來控制功能表的顯示的時候了。我們就假定您產生的三個數組分別命名為arrUnit,arrDept,arrEmp。

  四、編寫JavaScript代碼,控制功能表的顯示

  其實有經驗的程式員,讀到這裡應該知道如何進行下去。但您不妨讀下去,也許,筆者的方法對您未必不是一種新的嘗試。而且,據我猜測,讀我這篇文章的大多數都是沒有經驗的程式員,呵呵,幫人幫到底吧。Come On, Let's Go.

  讓菜單顯示出來,其實有好幾種思路。利用ASP等程式直接產生<SELECT>結構、利用OPTION對象的ADD和Remove方法動態添加和改變等等,都是可以使用的方法。但,經過筆者的多次實踐和摸索,有一種方法更為有效,那就是利用Script代碼動態改寫整個<SELECT>架構。

  好,就讓我們從載入頁面(document)開始,一步一步的講解JavaScript代碼到底是如何控制功能表的顯示的。

  既然有三個菜單,那麼我們就得事先設計出這樣的HTML代碼(其實要不要無所謂,放在那裡只是為了便於理解):

<BODY BGCOLOR="#FFFFFF" >
...
<TD>
<SELECT NAME="s0" ></SELECT>
</TD>
<TD>
<SELECT NAME="s1" ></SELECT>
</TD>
<TD>
<SELECT NAME="s2" ></SELECT>
</TD>
</BODY>

  您有可能要問,這裡怎麼什麼資料都沒有?不要奇怪,等一下您自然就會明白。我們來看一下<BODY>對象的ONLOAD事件body_onload()做了些什麼工作?

function body_onload(){
var TD = GetParent(document.all("s0"), "TD");
TD.innerHTML = MakeMenu(arrUnit, 0, 0, "s0", 1);
TD = GetParent(document.all("s1"), "TD");
TD.innerHTML = MakeMenu(arrDept, GetSelectValue(document.all("s0")), 0, "s1", 1);
TD = GetParent(document.all("s2"), "TD");
TD.innerHTML = MakeMenu(arrEmp, GetSelectValue(document.all("s1")), 0, "s2", 1);
}

  讓我們來研究一下。首先程式利用GetParent()函數取得s0的容器TD物件控點,然後,利用MakMenu()函數產生菜單代碼,並把代碼賦值給剛才取得的TD對象;然後是s1,接著是s2.。GetParent()函數定義如下:
function GetParent(src, tag){
if (src && src.tagName!=tag){
return(GetParent(src.parentElement, tag));
}
return src;
}
  這裡的tag參數必須大寫,例如TD、TR、TABLE,函數返回的是離src指定的元素最近的由tag標籤定義的父物件。

  我們要特別說明一下MakeMenu()函數,這個函數的作用不言而喻--產生菜單的HTML定義,先看看函數定義:

function MakeMenu(arrSub, pValue, cValue, name, bulSkip){
var sHTML = "<select name='" + name + "' >";
if (bulSkip) sHTML += "<option value=0><未選擇></option>";
for (var i=0; i < arrSub.length; i++){
if (arrSub[i][0]==pValue){
var tag = (arrSub[i][1]==cValue)?" selected>":">";
sHTML += "<option value='" + arrSub[i][1] + "'" + tag + arrSub[i][2] + "</option>";
}
}
sHTML += "</select>";
return sHTML;
}

代碼拷貝框
function MakeMenu(arrSub, pValue, cValue, name, bulSkip){var sHTML = "<select name='" + name + "' >";if (bulSkip) sHTML += "<option value=0><未選擇></option>";for (var i=0; i < arrSub.length; i++){if (arrSub[i][0]==pValue){var tag = (arrSub[i][1]==cValue)?" selected>":">";sHTML += "<option value='" + arrSub[i][1] + "'" + tag + arrSub[i][2] + "</option>";}}sHTML += "</select>";return sHTML;}
[Ctrl+A 全部選擇 然後拷貝]

  來看一下參數的含義。arrSub,指的是菜單資料的來源,其實就是我們上文定義的數組;pValue,指定父結點的編號,根據這個編號,我們可以找出所有的子結點資料;cValue,指定菜單的預設顯示項目;name,指定產生的<SELECT>菜單的名稱;bulSkip,指定菜單的預設顯示項目是"<未選擇>"還是具體資料。

  GetSelectValue()函數的目的,就是取得<SELECT>對象當前顯示的值。如果沒有顯示任何值,函數返回0。

function GetSelectValue(oSelect){
if (oSelect.selectedIndex < 0) return 0;
return oSelect.options(oSelect.selectedIndex).value;
}

  那麼,當使用者載入頁面之後,首先啟動並執行就是body_onload()函數,該函數根據已經產生的JavaScript多維陣列,利用MakeMenu()函數動態產生菜單的HTML代碼,並根據DHTML的原理載入到頁面中。OK,運行一下頁面,看看菜單是否正常顯示?如果有什麼問題,抓緊時間好好調試一下,例如資料庫的串連是否正常,javascript代碼的大小寫是否正確,數組的定義是否有什麼問題…

  下一步,選擇菜單…等一下,好像還有什麼遺漏,對了,我們還必須為<SELECT>對象的onchange事件添加程式碼:

function SetSubMenu(pSelect){
var oOption, sValue;
if (pSelect.selectedIndex < 0) return;
switch (pSelect.name){
case "s0":
var TD = GetParent(document.all("s1"), "TD");
TD.innerHTML = MakeMenu(arrDept, GetSelectValue("s0"), "0", "s1", 0);
TD = GetParent(document.all("s2"), "TD");
TD.innerHTML = MakeMenu(arrEmp, GetSelectValue("s1"), "0", "s2", 0);
break;
case "s1":
var TD = GetParent(document.all("s2"), "TD");
TD.innerHTML = MakeMenu(arrEmp, GetSelectValue("s1"), "0", "s2", 0);
break;
default:
}
}

  好了,我們再檢查一下,還有沒有什麼遺漏。從第一個下拉式功能表中選擇單位,立即,第二個下拉式功能表和第三個下拉式功能表都發生了變化,看看是不是想要的。(不是?呵呵,回頭好好檢查);再在第二個下拉式功能表中選擇部門,看看員工的下拉式功能表是否跟著改變?

  恭喜你,你已經成功的實現了三維下拉式功能表。其實,對於二維菜單,實現的方法完全一致。讀者完全可以利用本文的方法實現WEB項目菜單的全攻略。以後再遇到類似的問題,我想這回你一定可以毫不猶豫的說,讓我來搞定它。



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.