我真的很喜歡那些將幾種不同的技術(shù)集成在一個(gè)小應(yīng)用程序中的綜合示例。
在本月的 Code Corner 中,我們即將進(jìn)行這樣的工作,即將“可擴(kuò)展的標(biāo)記語(yǔ)言 (XML)”、“可擴(kuò)展的樣式表語(yǔ)言 (XSL)”、JScript? 以及“層疊樣式表 (CSS)”結(jié)合在一起,創(chuàng)建一個(gè)分層的動(dòng)態(tài) HTML(DHTML) 目錄 (TOC)。既然我們已經(jīng)涉及到了這些縮寫詞,我們有意識(shí)地舉一個(gè)簡(jiǎn)單示例 — 并且,您可能已經(jīng)對(duì)各項(xiàng)技術(shù)非常熟悉了。如果您已熟悉,則希望當(dāng)您看見(jiàn)它們結(jié)合在一起使用時(shí)會(huì)發(fā)現(xiàn)某些價(jià)值所在;如果您還不熟悉,也許這個(gè)例子會(huì)激起您對(duì)這些內(nèi)容進(jìn)一步探索的興趣。
對(duì) Web 開(kāi)發(fā)人員而言,通過(guò)使用帶有“有意義的”標(biāo)記數(shù)據(jù)元素的 XML,可實(shí)現(xiàn)使信息更有攜帶性與彈性。由于在 Internet Explorer 5 中增強(qiáng)的 XSL 支持,使得在瀏覽器中顯示 XML 數(shù)據(jù)更加容易。
我們?cè)?Web Workshop 中使用 XML 存儲(chǔ) TOC 信息已經(jīng)有一段時(shí)間了,通過(guò) XSL 樣式表可以將該信息轉(zhuǎn)換為 HTML。樣式表還“寫下”鏈接到一個(gè) CSS 和 JScript 文件,因此我們一次就可將 XML 轉(zhuǎn)換到 DHTML。通過(guò)修改單一的 XSL 樣式表,XML 存儲(chǔ)的數(shù)據(jù)即可輕松改變所有 TOC 的輸出格式。
讓我們來(lái)依次看一下這四種文件 — XML、XSL、JScript 及 CSS。
對(duì)于本示例,我們已經(jīng)創(chuàng)建了一個(gè)與 web 開(kāi)發(fā)有關(guān)的文章或“主題”的列表。每個(gè) TOPIC 元素都有一個(gè)說(shuō)明性的 TITLE 和 URL。主題按 TOPICS 元素內(nèi)的 TYPE 進(jìn)行分組。注意第三個(gè) TOPICS 元素自身包含 TOPICS 元素。webdev.xml 文件頂端的<?xml:stylesheet type="text/xsl" href="list.xsl"?>處理指令會(huì)告訴 Internet Explorer 5 當(dāng)該 XML 文件直接在瀏覽器中打開(kāi)時(shí),按照此樣式表實(shí)施 XML。(我們將在欄目尾端討論如何在服務(wù)器上以 ASP 實(shí)現(xiàn)此項(xiàng)操作。)
以下是 XML 數(shù)據(jù):
<?xml version="1.0"?><?xml:stylesheet type="text/xsl" href="list.xsl"?><TOPICLIST TYPE="Web Dev References"><TOPICS TYPE="DHTML"><TOPIC><TITLE>Objects</TITLE><URL>/workshop/author/dhtml/reference/objects.asp</URL></TOPIC><TOPIC><TITLE>Properties</TITLE><URL>/workshop/author/dhtml/reference/properties.asp</URL></TOPIC><TOPIC><TITLE>Methods</TITLE><URL>/workshop/author/dhtml/reference/methods.asp</URL></TOPIC><TOPIC><TITLE>Events</TITLE><URL>/workshop/author/dhtml/reference/events.asp</URL></TOPIC><TOPIC><TITLE>Collections</TITLE><URL>/workshop/author/dhtml/reference/collections.asp</URL></TOPIC></TOPICS><TOPICS TYPE="CSS"><TOPIC><TITLE>Attributes</TITLE><URL>/workshop/author/css/reference/attributes.asp</URL></TOPIC><TOPIC><TITLE>Length units</TITLE><URL>/workshop/author/css/reference/lengthunits.asp</URL></TOPIC><TOPIC><TITLE>Color table</TITLE><URL>/workshop/author/dhtml/reference/colors/colors.asp</URL></TOPIC></TOPICS><TOPICS TYPE="XML"><TOPICS TYPE="XML DOM"><TOPIC><TITLE>Developer‘s guide</TITLE><URL>/xml/XMLGuide/default.asp</URL></TOPIC><TOPIC><TITLE>Objects</TITLE><URL>/xml/reference/scriptref/XMLDOM_Objects.asp</URL></TOPIC></TOPICS><TOPICS TYPE="XSL"><TOPIC><TITLE>Developer‘s guide</TITLE><URL>/xml/XSLGuide/default.asp</URL></TOPIC><TOPIC><TITLE>Elements</TITLE><URL>/xml/reference/xsl/XSLElements.asp</URL></TOPIC><TOPIC><TITLE>Methods</TITLE><URL>/xml/reference/xsl/xslmethods.asp</URL></TOPIC><TOPIC><TITLE>Pattern syntax</TITLE><URL>/xml/reference/xsl/XSLPatternSyntax.asp</URL></TOPIC></TOPICS></TOPICS></TOPICLIST>
雖然我們可以在代碼中直接對(duì) XML 進(jìn)行操作,但 XSL 讓我們使用陳述的方法,將 XML 轉(zhuǎn)換到顯示輸出(在此例中為 HTML),這種方法幾乎不需要代碼(和勞神)。XSL 使您不必編寫很多涉及分支的代碼 —如果您在 XML 文件中嵌套了復(fù)雜的層次,則此效果會(huì)特別明顯。在此例中,我們可以使用任意數(shù)目的 TOPICS 層次;XSL 均可很好地對(duì)此進(jìn)行處理。
讓我們來(lái)看一下 XSL 代碼。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"><xsl:template match="/"><HTML><HEAD><TITLE>List <xsl:value-of select="TOPICLIST/@TYPE" /></TITLE><LINK REL="stylesheet" TYPE="text/css" HREF="list.css" /><SCRIPT TYPE="text/javascript" LANGUAGE="javascript" SRC="list.js"></SCRIPT></HEAD><BODY><BUTTON ONCLICK="ShowAll(‘UL‘)">Show All</BUTTON><BUTTON ONCLICK="HideAll(‘UL‘)">Hide All</BUTTON><H1>List <xsl:value-of select="TOPICLIST/@TYPE" /></H1><UL><xsl:apply-templates select="TOPICLIST/TOPICS" /></UL><P><BUTTON ONCLICK="window.alert(document.body.innerHTML);">View HTML</BUTTON></P></BODY></HTML></xsl:template><xsl:template match="TOPICS"><LI CLASS="clsHasKids"><xsl:value-of select="@TYPE" /><UL><xsl:for-each select="TOPIC"><LI><A TARGET="_new"><xsl:attribute name="HREF">http://msdn.microsoft.com<xsl:value-of select="URL" /></xsl:attribute><xsl:value-of select="TITLE" /></A></LI></xsl:for-each><xsl:if test="TOPICS"><xsl:apply-templates /></xsl:if></UL></LI></xsl:template></xsl:stylesheet>
如您所見(jiàn),我們使用了眾多 HTML 標(biāo)記符和少量的 "xsl:" 元素。我們明確告訴處理器這是一個(gè) XSL 文件并作為 XSL 文件的開(kāi)始,然后聲明兩個(gè) xsl:template 元素塊,<xsl:template match="/"> 與 <xsl:template match="TOPICS">。
第一個(gè)塊 <xsl:template match="/"> 通知處理器從由斜杠指示的 XML 文件的根目錄開(kāi)始轉(zhuǎn)換。在本塊中,我們要做兩件事:“寫下”我們的 HTML 輸入的結(jié)構(gòu)和類屬元素,并有條件地掛接到任何其他模板塊中。我們采用 TOPICLIST 元素 TYPE 屬性的值 — Web 開(kāi)發(fā)參考 — 并將其放入 <TITLE> 和 <H1> HTML 元素中;我們還掛接腳本 (list.js) 和樣式表 (list.css) 文件,它們將提供對(duì) DHTML 外觀和行為的處理。我們使用腳本文件中的兩個(gè)函數(shù),在頂端繪制了一些供用戶顯示或隱藏所有 TOC 節(jié)點(diǎn)的按鈕,在底端添加了另一個(gè)按鈕,來(lái)查看警報(bào)框中的 HTML 源文件。
第一個(gè)塊中最有趣的元素是 <xsl:apply-templates select="TOPICLIST/TOPICS" />。它通知處理器轉(zhuǎn)換 XML 文檔的所有 TOPICS 節(jié)點(diǎn),這些節(jié)點(diǎn)是根目錄 TOPICLIST 節(jié)點(diǎn)的子節(jié)點(diǎn)。如果您查看一下上面的 XML 文件,您會(huì)注意到它是由 TYPE "DHTML"、"CSS" 和 "XML" 的 TOPICS 節(jié)點(diǎn)組成的。在這里,XSL 處理器將從此第一個(gè)塊開(kāi)始分支,并在樣式表中搜索另一個(gè) xsl:template 塊,它與我們的 select="TOPICLIST/TOPICS" 屬性相匹配。正如您可能猜到的,這就是我們的第二個(gè)塊。
第二個(gè)模板塊 <xsl:template match="TOPICS"> 才是樂(lè)趣的開(kāi)始。我們的三個(gè)節(jié)點(diǎn)的 TOPICS 中的每一個(gè)的上下文從第一個(gè)塊傳遞到本塊,并在此得以處理。首先,我們打開(kāi)一個(gè) HTML 列表項(xiàng)元素 (LI),并賦予其 "clsHasKids" 的 CLASS 屬性,我們后面將在 DHTML 中用到它;然后,我們輸出正在處理的 TOPICS 節(jié)點(diǎn)的 TYPE,這是通過(guò) <xsl:value-of select="@TYPE" /> 處理的。為獲取 XSL 中的節(jié)點(diǎn)值,使用了 xsl:value-of 元素。如果獲取了某一屬性的值,則屬性名的開(kāi)始處將會(huì)加上一個(gè) "@"。
接下來(lái),我們打開(kāi)一個(gè) HTML 無(wú)序列表 (<UL>),它將包括當(dāng)前 TOPICS 元素的所有子元素。使用 xsl:for-each 循環(huán)結(jié)構(gòu),它也可以將每個(gè) TOPIC 子元素上下文更改為該元素(此時(shí)為 TOPIC),我們可以創(chuàng)建一個(gè) <LI> 元素,并將其 TITLE 包在一個(gè) A 鏈接中輸出,該鏈接的 HREF 屬性被設(shè)為 URL 元素的值。如果要以 XSL 將某一屬性動(dòng)態(tài)添加到一個(gè) HTML 輸出元素中,我們可以使用 xsl:attribute,其 name 屬性設(shè)為我們希望創(chuàng)建的 HTML 屬性;正如:<xsl:attribute name="HREF">。我們用服務(wù)器名稱(http://msdn.microsoft.com)以及 TOPIC URL, <xsl:value-of select="URL" /> 植入 HREF 值。
好了,我們已經(jīng)寫出了頂端組中的所有鏈接,但我們還沒(méi)有涉及 TOPICS 的子 TOPICS — 主題的嵌套列表。這是一個(gè) XSL 真正表現(xiàn)出色的區(qū)域,它可以使用一條語(yǔ)句處理所有的遞歸!關(guān)閉進(jìn)入第二個(gè)塊先開(kāi)始處理 TOPICS 元素時(shí)打開(kāi)的容器 <UL> 及其父級(jí) <LI> 之前, 我們要聲明 <xsl:if test="TOPICS"><xsl:apply-templates /></xsl:if>。通過(guò)使用將上下文更改為我們正在測(cè)試的元素的 xsl:if 條件語(yǔ)句,我們可以探詢?cè)摌?shù)是否存在一個(gè)子 TOPICS 節(jié)點(diǎn)。如果存在,則 <xsl:apply-templates /> 將通知處理器無(wú)論當(dāng)前上下文是什么都與之匹配。由于我們已詢問(wèn)了 TOPICS,如果此子節(jié)點(diǎn)存在,則再次與這第二個(gè)塊相匹配,然后循環(huán)返回到其中去。這樣,在第三個(gè) TOPICS 元素 "XML" 的情形中,匹配兩個(gè)子 TOPICS 元素,然后重新進(jìn)入這個(gè) XSL 塊,以便將這些內(nèi)容輸出到 HTML。相應(yīng)的嵌套會(huì)自動(dòng)得以保留。很棒,是不是?
請(qǐng)看直接來(lái)自轉(zhuǎn)換的 HTML 輸出。
XSL 代碼就討論到這里。我們已經(jīng)通過(guò)在 XML 文檔內(nèi)的遞歸生成了一組嵌套的 HTML 列表。既然我們已經(jīng)得到了該輸出,則讓我們現(xiàn)在來(lái)快速瀏覽一下使 TOC 變?yōu)閯?dòng)態(tài) .js 和 .css 的代碼。
共有四個(gè)函數(shù)處理我們的 HTML TOC 中的所有“操作”。ShowAll() 和 HideAll() 只需要一個(gè) tagName 參數(shù),它們?cè)陧?yè)面上構(gòu)建一個(gè)該標(biāo)記(除了第一個(gè))的所有元素的集合,然后將每個(gè)元素的 style.display 屬性設(shè)定為 "block"(顯示)或 "none"(隱藏)。前兩個(gè)函數(shù),document.onclick() 和 GetChildElem() 協(xié)同運(yùn)行,以顯示或隱藏父 <LI> 被單擊的某個(gè) <UL>。實(shí)際上我們可能已經(jīng)在 XSL 中編寫了每個(gè) <LI> 的 ONCLICK 屬性,但像我們的同事 DHTML Dude (英文)一樣,我們喜歡 Internet Explorer 事件排序的抽象、能力和靈活性。
document.onlclick() 將文檔的 onclick() 事件與本函數(shù)聯(lián)系起來(lái),因此在文檔上的單擊都在此得以處理?,F(xiàn)在,我們只希望在單擊帶有子 <UL> 的 <LI> 時(shí)執(zhí)行某種操作,我們首先檢查用戶是否單擊了類名 為 "clsHasKids" 的元素。然后,執(zhí)行一些預(yù)防性的異常處理后,使用 GetChildElem() 要么返回對(duì)某個(gè)有效子 <UL> 的引用,要么返回錯(cuò)誤,如果它不存在的話。最后,我們使用三組 JScript 條件語(yǔ)句將 style.display 設(shè)為其當(dāng)前設(shè)置的補(bǔ)充。
function GetChildElem(eSrc,sTagName){var cKids = eSrc.children;for (var i=0;i<cKids.length;i++){if (sTagName == cKids[i].tagName) return cKids[i];}return false;}function document.onclick(){var eSrc = window.event.srcElement;if ("clsHasKids" == eSrc.className && (eChild = GetChildElem(eSrc,"UL"))){eChild.style.display = ("block" == eChild.style.display ? "none" : "block");}}function ShowAll(sTagName){var cElems = document.all.tags(sTagName);var iNumElems = cElems.length;for (var i=1;i<iNumElems;i++) cElems[i].style.display = "block";}function HideAll(sTagName){var cElems = document.all.tags(sTagName);var iNumElems = cElems.length;for (var i=1;i<iNumElems;i++) cElems[i].style.display = "none";}
最后,我們使用一點(diǎn) CSS 小技巧來(lái)美化 TOC 的外觀。本示例中的 CSS 相當(dāng)簡(jiǎn)單;您可以容易地完成一些更為復(fù)雜(也更為漂亮)的工作,如 Web Workshop(英文)中的 TOC。這里的任意結(jié)果的樣式規(guī)則僅適用于 <UL> 和 <LI> 的。我們將所有是 <LI> 的子元素的 <UL> 元素的最初顯示設(shè)為 "none",將給那些 className 為 "clsHasKids" 的 <LI> 元素賦予“手”形光標(biāo),表示單擊它會(huì)完成某種操作(這里表示顯示或隱藏子 <UL>), 并調(diào)整 <UL> 和 <LI> 的列表樣式類型和頁(yè)邊距。
BODY { font-family:verdana; font-size:70%; }H1 { font-size:120%; font-style:italic; }UL { margin-left:0px; margin-bottom:5px; }LI UL { display:none; margin-left:16px; }LI { font-weight:bold; list-style-type:square; cursor:default; }LI.clsHasKids { list-style-type:none; cursor:hand; }A:link, A:visited, A:active { font-weight:normal; color:navy; }A:hover { text-decoration:none; }BUTTON { font-family:tahoma; font-size:100%; }
正如上面提到的,在 XML 存儲(chǔ)數(shù)據(jù)的一個(gè)優(yōu)點(diǎn)就是可以使用不同的樣式表進(jìn)行轉(zhuǎn)換,很容易地生成同一數(shù)據(jù)的不同視圖??上螺d的示例代碼包括四個(gè)附加的 XSL 樣式表(以及腳本和樣式文件):divs.xsl 可以直觀的層次 DIV 生成 TOPIC,flat.xsl 在相同層次上生成所有 TOPIC,links.xsl 打印某個(gè) TABLE 中的實(shí)際 URL,而 list_pp.xs、l 則對(duì) list.xsl 稍作修改,以便生成較規(guī)整的 HTML 源代碼。
示例是基于在客戶機(jī)的 Internet Explorer 5 上運(yùn)行而編寫的,它使用了 XML mime-type。無(wú)論怎樣,在跨瀏覽器的情形中,在服務(wù)器上轉(zhuǎn)換 XML 并將其下傳“確實(shí)”是很容易的。下面的代碼(它也在可下載的 .zip 文件中)就是針對(duì)這一問(wèn)題的。用戶只需將示例文件放在 Internet Information Server(IIS) 服務(wù)器上,裝載 webdev.asp, 并傳遞一個(gè) XSL 文件名作為可選查詢字符串參數(shù);例如,http://<yourmachinename>/codecorner/xml/webdev.asp?xsl=divs.xsl。您還可以裝載 default.asp 頁(yè)面,該頁(yè)面有一個(gè)到各種 XML/XSL 組合的鏈接列表。
<% @LANGUAGE="JScript" %><%var sXml = "webdev.xml"var sXsl = new String(Request.QueryString("xsl"));if ("undefined" == sXsl) sXsl = "list.xsl"var oXmlDoc = Server.CreateObject("MICROSOFT.XMLDOM");var oXslDoc = Server.CreateObject("MICROSOFT.XMLDOM");oXmlDoc.async = false;oXslDoc.async = false;oXmlDoc.load(Server.MapPath(sXml));oXslDoc.load(Server.MapPath(sXsl));Response.Write(oXmlDoc.transformNode(oXslDoc));%>
有關(guān)從 XML 創(chuàng)建 DHTML,或者其他使用 XML 的有趣信息,可訪問(wèn) MSDN Online Web Workshop(英文) 的 XML(英文)區(qū),特別是本示例中突出顯示的那些鏈接。還請(qǐng)務(wù)必留意我的同事 Charlie Heinemann 的定期 Extreme XML(英文) 專欄,它在 MSDN Online Voices 中。
George Young 是 Microsoft 的 MSDN Online 站點(diǎn)的開(kāi)發(fā)負(fù)責(zé)人,以前曾在 Site Builder Network 站點(diǎn)工作。在他有空時(shí),喜歡用 Windows 媒體播放器收聽(tīng)墨西哥的廣播電臺(tái),也常開(kāi)著他的寶貝卡迪車往返于新奧爾良和華盛頓之間的 Redmond。
聯(lián)系客服