使用XML/HTC/DHTML類比標準Windows菜單
來源:互聯網
上載者:User
dhtml|htc|window|xml|標準|菜單
隨著internet的發展,XML作為一種跨平台的通用結構化資料描述語言 (Data Description Language)越來越得到人們的重視,並已經得到了廣泛應用,如MicroMedia公司出品的Dreamweaver、Flash以及遊戲搶灘登入等軟體都利用了XML檔案作為資料存放區方式,而且Microsoft.NET也是架構在XML上面的。目前出現的取代HTML語言的下一代網頁製作語言XHTML(可擴充超文字標記語言 (HTML)),
就是建立在XML基礎上,因此掌握XML技術是未來網頁製作者必備技能。
本文通過一個類比標準Windows菜單程式來介紹有關XML技術應用。
一、 程式原理
考慮到層疊菜單複雜性,為使程式簡化,只類比到二級子功能表。下面我們來看看標準windows菜單過程:滑鼠移入菜單時反白,移出時回複原狀,點擊則菜單凹陷顯示並開啟子功能表,滑鼠點擊子功能表則完成相應功能。
當子功能表開啟後移動滑鼠,則移入菜單凹陷顯示並開啟相應子功能表,移出菜單則回複原狀並隱藏相應子功能表,若滑鼠並不是移動到另外菜單而是在頁面其它地方,則移出菜單仍凹陷顯示,相應子功能表也不隱藏,只有當在非菜單處點擊時才恢複原狀並隱藏子功能表。標準Windows菜單一個重要特點就是子功能表開啟後,
在滑鼠移出菜單時並不隱藏或者在文檔中非菜單處任意單擊時才隱藏。目前很多網頁菜單程式都做不到這點,一般都是移入菜單時開啟子功能表,移出菜單時隱藏子功能表。採用IE5.0中新提供的滑鼠捕獲技術就可以完全解決這個問題。所謂滑鼠捕獲,就是與滑鼠相關的所有事件都由設定滑鼠捕獲的對象進行處理,而無論這些事件是否由該對象觸發。
由於xml文檔非常適合描述結構化資料,故我們使用xml文檔儲存菜單及一級子功能表的有關資訊。
為便於在其它頁面使用類比標準Windows菜單,我們並不使用代碼功能重用效能比較差的外部指令碼包含檔案方法,而是採用IE 5.0新引入的DHTML行為(Behavior)技術將菜單功能代碼封裝在一個HTML 行為組件(HTC)內。 HTML行為組件封裝了頁面上特定的功能或動作,當把某個行為組件附加到頁面上的標準HTML元素(對象)時,將增強該元素(對象)的預設功能,使得該元素(對象)含有行為的屬性、方法或事件,這使代碼重用變得十分容易,並簡化了頁面的HTML代碼,提高了頁面的可管理性。
因篇幅所限,有關xml文檔及HTML行為組件的更多細節請參閱有關資料,本文不予詳述。
二、程式實現
1.首頁面(index.htm)
我們首先在首頁面中定義充當菜單容器的Div元素,並使用CSS中的behavior屬性附加行為組件(Menu.htc),這樣就可以使用元素擴充的屬性xmlsrc來指定儲存菜單資訊的資料檔案(Menu.xml)以及使用onMenuClick來擷取的群組件觸發的新事件並調用相應事件處理函數。
此外為簡化程式,在首頁面中定義了突出(Up_Menu)、凹陷(Dn_Menu)、原狀(Menu)三種菜單外觀樣式,在實際應用時應在組件中定義菜單顯示外觀,這樣更符合組件開發原則。
2.菜單組件(Menu.htc)
類比標準Windows菜單的功能主要由菜單組件提供。菜單由以下Html元素組成:充當菜單容器的Div元素(在首頁面定義)、用於一級功能表項目的span元素(初始化菜單時建立)、包含二級功能表項目的Div元素(單擊一級菜單時建立)、二級功能表項目的Div元素(單擊一級菜單時建立)。在該組件中定義了用於指定菜單資料檔案的xmlsrc屬性、點擊二級菜單時觸發的onMenuClick事件。
當首頁面解析完畢後觸發ondocumentready事件,組件捕獲這個事件並呼叫事件處理MenuInit()函數初始化一級菜單。當觸發滑鼠事件時,如onmouseover、onmouseout、onmouseclick等,組件將分別捕獲這些事件並調用相應的事件處理函數MenuOver()、MenuOut()、MenuClick()。在這些處理函數中首先通過Event.srcElement屬性確定觸發事件的對象,然後依據對象type類型(如parentMenu、subMenu或其它)分別做相應處理,
如更改菜單顯示外觀、開啟或隱藏子功能表、觸發新事件等。
在一級菜單觸發有關滑鼠事件後,開啟二級菜單並通過setCapture()方法對一級菜單對象設定滑鼠捕獲。此後所有在該菜單或者文檔任意位置觸發的onmouseover、onmouseout、onmouseclick滑鼠事件都由該菜單對象負責處理,但event.srcElement屬性仍是觸發滑鼠事件的對象而不是設定滑鼠捕獲的對象。當在二級子功能表或文檔中非菜單處任意單擊時,隱藏二級菜單並通過releaseCapture()方法釋放滑鼠捕獲。
單擊二級菜單時,其事件處理函數onmouseclick通過createEventObject方法建立一個event(事件)對象,再通過fire方法觸發新事件,並利用event對象的有關屬性傳遞資訊(如event.menuId返回所選定的菜單ID),
然後由容器捕獲這個事件,並調用相應事件處理函數HanderSubMenu ()。在首頁面中定義函數HanderSubMenu ()能夠使菜單組件更具通用性。
3.菜單資料檔案(Menu.xml)
Item節點儲存一級菜單資訊,SubItem節點儲存二級菜單資訊,每個節點都含有兩個屬性:value(菜單ID)、name(菜單名稱)。
===================================================
該程式必須在IE5.0及以上版本瀏覽器運行,運
行結果如圖。怎麼樣?象不象標準Windows菜單。
附:類比標準Windows菜單原始碼(在Win98+IE5.0下通過)
===================================================
<Html><Head>
<Title>菜單組件樣本</Title>
<style>
<!--.Menu{
border-top:2 solid #CCCCCC;
border-bottom:1 solid #CCCCCC;
border-left:2 solid #CCCCCC;border-right:1 solid #CCCCCC;
}
.Up_Menu {
border-top:2 outset;border-left:2 outset;border-right:1 outset #FFFFFF;
border-bottom:1 outset #FFFFFF;
background-color:#CCCCCC; }
.Dn_Menu{
border-top:2 inset ;
border-left:2 inset ;
border-right:1 inset ;
border-bottom:1 inset ;
background-color:#CCCCCC;
}--></style><script>
function HanderSubMenu(){//選擇子功能表後相應處理 switch(event.menuId){ case event.menuId: alert(event.menuId); break; }}</script></Head><Body style="background-color:#CCCCCC;"><TABLE style="font-size:12px;">
<TBODY><TR height="10" >
<TD Align="center" height=25 style="border-top:2px groove;border-bottom:2px groove;"> <div id="Menu" style="behavior:url(Menu.htc)" xmlsrc="Menu.xml" onMenuClick="HanderSubMenu()"></div>
</TD> </TR>
</TBODY></TABLE></Body></Html>
================================================
Menu.htc檔案:
<PUBLIC:COMPONENT>
<PUBLIC:PROPERTY Name="xmlsrc" PUT="putxmlsrc"/>
<PUBLIC:EVENT Name="onMenuClick" ID="menuClick" />
<PUBLIC:ATTACH EVENT="ondocumentready" For="element" ONEVENT="MenuInit()" />
<PUBLIC:ATTACH EVENT="onmouseover" for="element" ONEVENT="MenuOver()" />
<PUBLIC:ATTACH EVENT="onmouseout" for="element" ONEVENT="MenuOut()" />
<PUBLIC:ATTACH EVENT="onclick" for="element" ONEVENT="MenuClick()" />
<Script language="JavaScript">
var xmldoc=null;// xml文檔物件變數
var activeMenu=null;//開啟子功能表的父菜單物件變數
var menuContainer=null;//包含子功能表項容器物件變數
function putxmlsrc(str){//載入菜單資料檔案
xmldoc= new ActiveXObject("Microsoft.XMLDOM");//建立xml文檔對象
xmldoc.async=false; xmldoc.load(str);//載入菜單資料}
function MenuInit(){//初始化菜單
var parentMenuItems = xmldoc.selectNodes("//Itemlist/Item"); file://讀取一級菜單資料
var xmlElement = parentMenuItems.nextNode();
var newElement;
while (xmlElement != null){ newElement = document.createElement("span"); file://建立菜單元素
newElement.type = "parentMenu";
with (newElement){ innerHTML = xmlElement.getAttribute("name");
id=xmlElement.getAttribute("value");
className="Menu"; }
with (newElement.style){
position="relative";
width= 60;
cursor="default"; }
element.appendChild(newElement); file://element指附加行為的元素
xmlElement = parentMenuItems.nextNode(); }}
function MenuOver(){ var EventSource=event.srcElement;//捕獲觸發事件的對象
switch(activeMenu){//判斷子功能表是否存在
case null://不存在子功能表
if(EventSource.type=="parentMenu")
EventSource.className="Up_Menu";
break;
default:
switch(EventSource.type){//判斷觸發事件物件類型
case "parentMenu"://移入到一級菜單
if(activeMenu!=EventSource){//判斷是否移入到新一級菜單
removeSubMenu();//刪除原有子功能表
buildSubMenu(EventSource.id);//建立新子功能表
EventSource.setCapture();//對新菜單設定滑鼠捕獲
activeMenu.className="Menu";
activeMenu=EventSource; file://更新開啟子功能表的父菜單物件變數
activeMenu.className="Dn_Menu"; }
break;
case "subMenu"://移入到子功能表
EventSource.style.color="#FF0000";
EventSource.style.textDecoration="underline";
break; } break; }}
function MenuOut(){ EventSource=event.srcElement
switch(activeMenu){ case null:
if(EventSource.type=="parentMenu")EventSource.className="Menu";
break;
default: if(EventSource.type=="subMenu"){
EventSource.style.color="#000000";
EventSource.style.textDecoration="none";
} break; }}
function MenuClick(){ EventSource=event.srcElement
switch(EventSource.type){
case "parentMenu":
if(activeMenu!=EventSource){
removeSubMenu();
buildSubMenu(EventSource.id);
EventSource.setCapture();
EventSource.className="Dn_Menu";
activeMenu=EventSource; }
break;
case "subMenu":
removeSubMenu();
activeMenu.className="Menu";
activeMenu.releaseCapture();//釋放滑鼠捕獲設定
activeMenu=null;
var eventObject = createEventObject();//建立事件對象
eventObject.menuId = EventSource.id;//通過事件對象屬性傳遞所選子功能表ID
menuClick.fire(eventObject);//將事件觸發到容器頁面 break;
default:
removeSubMenu();
if(activeMenu!=null){
activeMenu.releaseCapture();
activeMenu.className="Menu";
activeMenu=null; }
break; } }
function buildSubMenu(EventSourceid){//建立子功能表
menuContainer = document.createElement("div");
with (menuContainer.style){
position="absolute"; left=0; top=15; color="#000000"; cursor="default"; }
eval(EventSourceid).appendChild(menuContainer);
var subMenuItems = xmldoc.selectNodes("//Item[@value='"+EventSourceid+"']/SubMenu");
var xmlElement = subMenuItems.nextNode();
var newElement;
while (xmlElement != null){ n
ewElement = document.createElement("div");
newElement.type = "subMenu";
with (newElement){
innerHTML ="□ "+xmlElement.getAttribute("name");
id=xmlElement.getAttribute("value"); }
with (newElement.style){ width=75; height=18; }
menuContainer.appendChild(newElement);
menuContainer.className="Up_Menu";
xmlElement = subMenuItems.nextNode();
}}function removeSubMenu(){//刪除子功能表
if(menuContainer!=null)menuContainer.removeNode(true);}
</Script></Component>
===========================
Menu.xml檔案:
<?xml version="1.0" encoding="gb2312" ?>
file://xml處理指示指明為中文編碼,若不指明則不支援中文
<Itemlist> <Item value="file" name="檔案"
=======================