使用 XSLT 作為 HTML 的樣式表
簡介
當聽到樣式表這個詞時,您可能會想到 CSS 樣式表。XSLT 樣式表通常用於 XML 轉換,比如在 Web 服務之間對映資料。因為 XSLT 非常適合此用途,所以建立了頂層元素 <stylesheet> 的 <xsl:transform> 別名,雖然這很少使用。這種 XSLT 轉換的輸入結構與輸出結構有很大的不同。最重要的是,名稱空間的不同。
XSLT 樣式表的輸入結構與輸出結構相似,但卻更簡單些。其中已經擴充了一些標記,但大部分標記只是原樣複製到輸出。輸入和輸出的名稱空間是相同的 (HTML)。輸入文件也可以包含樣式表指令(比如建立腳註),這些指令屬於另一個名稱空間,不會傳遞到輸出中。
常用縮略語
- CSS:級聯樣式表
- XHTML:可擴充套件超文字標記語言
- XPath:XML 路徑語言
- XSLT:可擴充套件樣式表語言轉換
在本文中,我們將學習如何使用 XSLT 樣式表擴充 XHTML 文件。文中的示例展示瞭如何使用指令,如何引用其他源文件的部分,以及如何使用連結在主文件中導航。此外,我們還探索了頁面的解釋和編譯之間的區別。
CSS 樣式表的侷限性
XSLT 樣式表不會阻止您使用其他技術,比如 JavaScript 或 CSS。CSS 適用於字型、加粗、顏色、間距等。它不適合將來自不同位置的資訊組合在一起,比如腳註、模組或生成一個目錄。這正是 XSLT 的用武之地,它補充而不是替代了 CSS。
XSLT 用途示例
實際上,您可以將 XSLT 程式碼集中在一個檔案中。為了簡單起見,本文中的每個示例均位於一個獨立的 XSLT 檔案中,除了一些必要的程式碼。清單 1 給出了必需的程式碼。
清單 1. 必需的程式碼(位於 samples/common.xml 中)
<s:stylesheet xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/" xmlns:s="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="a h" version="1.0" > <s:template match="h:head"> <s:copy> <s:apply-templates select="@*|node()"/> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <link href="common.css" rel="stylesheet" type="text/css" /> </s:copy> </s:template> <s:template match="*"> <s:copy> <s:copy-of select="@*"/> <s:apply-templates/> </s:copy> </s:template> </s:stylesheet>
XHTML 的名稱空間定義了兩次:預設定義和 h:。預設名稱空間用於編寫輸出 XHTML 標記,其中應該避免使用名稱空間字首。h: 用在 XPath 表示式中。
本文使用 XSLT 1.0 版本。目前,大部分瀏覽器都無法解釋 XSLT 2.0。但是,如果 XSLT 執行在伺服器上,那麼它可能是一個實用的選擇。XSLT 2.0 還提供了:
- XPATH 2.0(if…then…else 和許多內建的函式)
- 內建和使用者編寫的 XPATH 函式
- 分組
在 清單 1 中:
- s:template match="head" 擴充了源文件的 head 一節,新增了一個 CSS 樣式表的連結。即使 UTF-8 是在 XML 中的預設編碼,一些瀏覽器也需要內容型別才能呈現它。
- s:template match="*" 是預設的詳細副本。原則上,所有內容都會複製到目標文件中。如果遺漏了此模板,只會將標記的文字內容複製到目標文件。不會複製處理指令節點。
本文中的所有其他示例都是匯入 common.xsl 的獨立檔案。
擴充
通過擴充,新增了一個未在源文件中顯式請求的特性。一個示例是 清單 1 中的 CSS 樣式表的連結。嘗試另一個示例,向每個內部連結新增一個小箭頭 (^ v),指明目標在它之前還是之後。清單 2 給出了該樣式表。
清單 2. 樣式表(在 samples/linkUpDown.xsl 中)
<s:stylesheet xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:s="http://www.w3.org/1999/XSL/Transform" version="1.0" > <s:import href="common.xsl"/> <s:template match="h:a[starts-with(@href,'#')]"> <s:copy> <s:copy-of select="@*"/> <s:variable name="name" select="substring-after(@href,'#')"/> <s:choose> <s:when test="preceding::h:a[@name=$name]"> <s:text>^</s:text> </s:when> <s:when test="following::h:a[@name=$name]"> <s:text>v</s:text> </s:when> </s:choose> <s:apply-templates/> </s:copy> </s:template> </s:stylesheet>
首先,匯入清單 2 中的通用樣式表。模板與內部連結(以 '#' 開頭)相匹配。如果連結指向的錨點位於連結之前,那麼使用一個向上箭頭擴充該連結(如果情況相反,則使用向下箭頭)。
s:copy-of 和 s:apply-templates 可確保不會沿途丟下任何內容。
清單 3 給出了一個示例文件(其中包含內部連結),它經過了清單 2 中的樣式表進行擴充。
清單 3. 源文件(在 samples/linkUpDown.xml 中)
<?xml-stylesheet href="linkUpDown.xsl" type="text/xsl"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head/> <body> <a name="a"/> <p>This link goes <a href="vb">downward.</a></p> <br/> <p>Reduce the size of the window to verify the link really works.</p> <br/> <a name="b"/> <p>This link goes <a href="^a">upward.</a> </p> </body> </html>
目標文件看起來相同,除了清單 4 中的條目。
清單 4. 目標文件(在 samples/linkUpDown.html 中)
… <a href="#b">v downwards.</a> … … <a href="#a">^ upwards.</a> …
指令
您可以在源文件中新增一些指令,告訴樣式表執行何種操作。它們屬於另一個名稱空間(在本例中為字首 a:),不會被複制到目標文件。
在清單 5 中,源文件中任何地方的指令標記 a:ref 都會建立一個腳註。
清單 5. 樣式表(在 samples/footnote.xsl 中)
<s:stylesheet xmlns="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:s="http://www.w3.org/1999/XSL/Transform" version="1.0" > <s:import href="common.xsl"/> <s:template match="h:body"> <s:copy> <s:apply-templates select="@*|node()"/ <!-- put the footnotes at the end if there is no a:references directive --> <s:if test="not(descendant::a:references)"> <s:call-template name="references"/> </s:if> </s:copy> </s:template> <!-- Create a footnote --> <s:template match="a:ref"> <s:variable name="number" select="count(preceding::a:ref) + 1"/> <a name="ref-{$number}"></a> <a class="footnote" href="#reference-{$number}"> <s:value-of select="concat('v ',$number)"/> </a> </s:template> <!-- if a:reference is missing, assume it at the end of the body --> <s:template match="a:references" name="references"> <hr/> <s:for-each select="//a:ref"> <s:variable name="number" select="count(preceding::a:ref) + 1"/> <p> <a name="reference-{$number}"></a> <a class="footnote" href="#ref-{$number}"> <s:value-of select="concat(' ^',$number)"/> </a> <s:apply-templates/> </p> </s:for-each> </s:template> </s:stylesheet>
使用源文件中的 a:references 指令,名為 references 的模板會在模板與該指令匹配的地方分配腳註。如果缺少這樣一個指令,第一個與 body 匹配的模板會在 body 的末尾分配腳註,方法是呼叫名為 references 的相同模板。在兩種情況下,都會列出腳註的內容,並生成一個由向上箭頭表示的向上連結。
第二個模板(匹配 a:ref)使用向下箭頭建立腳註的連結。腳註具有編號。這裡忽略了它的內容。
class="footnote" 屬性在 XSLT 轉換之後由一個 CSS 樣式表解析,該樣式錶連結在 XSLT 樣式表 common.xsl 中。
清單 6 中的源文件使用 a:ref 指令建立腳註。
清單 6. 源文件(在 samples/footnote.xml 中)
<?xml-stylesheet href="footnote.xsl" type="text/xsl"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/" > <head/> <body> <p> This example looks a little scientific <a:ref> From Latin <em>scientia</em> </a:ref> and academic <a:ref>From Greek akademia</a:ref>. </p> <p> Do you know why? <a:ref> It uses <em>footnotes</em>. </a:ref> </p> <p>Reduce size of window to verify links are generated.</p> </body> </html>
目標文件將腳註列表包含在底部,如清單 7 所示。
清單 7. 目標文件(在 samples/footnote.html 中)
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/"> <head><link type="text/css" rel="stylesheet" href="common.css"/></head> <body> <p>This example looks a little scientific <a name="ref-1"/><a href="#reference-1" class="footnote">v 1</a> and academic. <a name="ref-2"/><a href="#reference-2" class="footnote">v 2lt;/a> </p> <p>Do you know why? <a name="ref-3"/><a href="#reference-3" class="footnote">v 3</a> </p> <p>Reduce size of window to verify links are generated.</p> br/><br/> <hr/> <p><a name="reference-1"/><a href="#ref-1" class="footnote"> ^1</a> From Latin <em>scientia</em> </p> <p><a name="reference-2"/> <a href="#ref-2" class="footnote"> ^2</a>From Greek akademia</p> <p><a name="reference-3"/><a href="#ref-3" class="footnote"> ^3</a> It uses <em>footnotes</em>. </p> </body> </html>
突破源文件的邊界
也可引用其他源文件的其中一些部分。a:include 指令包含一個可能屬於另一個源文件的元素並轉換它,如清單 8 所示。
清單 8. 樣式表(在 samples/include.xsl 中)
<s:stylesheet xmlns="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/" xmlns:s="http://www.w3.org/1999/XSL/Transform" version="1.0" > <s:import href="common.xsl"/> <s:template match="a:include"> <s:choose> <s:when test="0!=string-length(@src)"> <s:apply-templates select="document(@src)//*[@id=current()/@refid]"/> </s:when> <s:when test="not(@src) and //a:default[1]/@src"> <s:apply-templates select="document(//a:default[1]/@src)//*[@id=current()/@refid]"/> </s:when> <s:when test="0=string-length(@src) or not(//a:default[1]/@src)"> <s:apply-templates select="//*[@id=current()/@refid]"/> </s:when> </s:choose> </s:template> </s:stylesheet>
源文件中的一個 a:include 指令引用源元素的 id。包含該元素的文件可在一個 src 屬性中命名。如果缺少該屬性,將使用 a:default 指令的 src 屬性。如果在任何地方都沒有 src 屬性,則使用同一個源文件。因此,refid 會引用 id 來避免無限的遞迴。
匯入的元素可能具有一種複雜的型別,並在包含 (apply-templates)之後進行轉換。清單 9、清單 10 和清單 11 給出了示例。
清單 9. 源文件(在 samples/include.xml 中)
<?xml-stylesheet href="include.xsl" type="text/xsl"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:a="http://sourceforge.net/projects/arbalo/"> <head> <a:default src="includedY.xml"/> </head> <body> <p>The following text is included:</p> <a:include refid="x" src="includedX.xml"/> <a:include refid="y1"/> <p id="i">double</p> <a:include refid="y2"/> <a:include refid="i" src=""/> </body> </html>
清單 10. 源文件的部分(在 samples/includeY.xml 中)
<h2 id="y2">I'm the <em>included</em> h2</h2> <h1 id="y1">I'm the <em>included</em> h1</h1>
清單 11. 目標文件(在 samples/include.html 中)
<body> <p>The following text is included:</p> <p id="x">I'm the <em>included</em> paragraph.</p> <h1 id="y1">I'm the <em>included</em> h1</h1> <p id="i">double</p> <h2 id="y2">I'm the <em>included</em> h2</h2> <p id="i">double</p> </body> </html>
主文件和導航
如果您有一個包含多個頁面的演示,有一個主文件包含頁面標題及其連結。您可以生成完整的導航,從每個頁面到任何其他頁面,以及到前一個和後一個頁面。這些細節不屬於本文的介紹範圍,但 參考資料 中提供了使用主文件的 HTML 演示的連結。可將 .xml 替換為 .html 來獲得編譯後的版本。讓瀏覽器向您顯示 .xml 的整潔原始碼。您會對它生成的原始碼量感到驚奇。
解釋與編譯的對比
解釋意味著頁面為 XML 格式(其副檔名為 .xml,其內容型別為文字/xml 或應用程式/xml),並且處理指令所引用的 XSLT 樣式表可在瀏覽器中執行。
編譯意味著瀏覽器看到的是 HTML(其副檔名為 .html,內容型別為文字/html),它是在請求頁面之前從您的開發環境中或伺服器上的 XML 轉換而來的。Xalan 和 Saxon 都是著名的 XSLT 處理器。
解釋是未來的發展方向。所有現代瀏覽器都支援 XSLT,並且它具有一些優點:
- 當測試時,您會立即獲得結果。只需在您測試的每個瀏覽器中按下 F5,即可反映源頁面、CSS 和 XSLT 樣式表的更改。
- 要傳遞給客戶端的資訊量減少了。
- 客戶端看到的是一個乾淨、整潔的網頁,因為還未生成擴充內容。
但也要注意一些缺點:
- 有一些舊瀏覽器可能不支援 XSLT。如果向一個受控的環境(內部網)釋出頁面,就不會出現問題。
- 一些現代瀏覽器禁止 XSLT 樣式表引用另一個目錄中的另一個樣式表。
- 將 XSLT 域其他功能相結合(比如 SVG 或 iframe)可能在一些瀏覽器中導致問題。
-
因為大部分瀏覽器都不支援 XSLT 2.0 或即將推出的 3.0,所以您無法使用新功能。沒有 XPath 2.0
if () then … else
,也沒有使用者編寫的 XPath 功能。
無論進行編譯還是解釋,頁面的其他轉換 (CSS、JavaScript) 都會在 XSLT 轉換之後執行。
結束語
在本文中,您學習瞭如何使用 XSLT 樣式表來擴充 XHTML 文件。您可以使用本文中的示例作為起點,構建您自己的 XSLT 樣式表。
下載
關於作者
Jürgen M. Regel 是位於德國漢諾威的 TUI InfoTec GmbH 的 Architecture Management & Software Engineering 部門的一名高階軟體工程師。他主要研究旅遊行業中的企業應用程式整合 (EAI)。
相關文章
- html樣式表三種引入方式HTML
- HTML樣式HTML
- HTML 樣式HTML
- HTML樣式插入、連結、表單標籤HTML
- html預設的樣式HTML
- 好看的表單HTML+CSS樣式,可直接執行HTMLCSS
- 在html中使用css樣式的集中方式HTMLCSS
- 使用XML+XSLT實現業務表單XML
- 使用樣式類為某一型別的元素設定樣式型別
- xslt函式詳解函式
- CSS層疊樣式表——定義樣式表CSS
- javascript控制html裡的元素樣式JavaScriptHTML
- HTML 樣式- CSS簡介HTMLCSS
- 操作樣式表
- CSS樣式表——佈局練習(製作360網頁)CSS網頁
- 使用jquery為指定元素新增樣式類jQuery
- 用XML和XSLT來生成靜態的HTML頁面XMLHTML
- 使用oracle utl_raw作為dump的逆函式Oracle函式
- XSLT實現XML文件轉換成HTML文件XMLHTML
- java生成帶html樣式的word檔案JavaHTML
- html表單的使用方法HTML
- Qt更改按鈕樣式 (以QSpinBox使用左右按鈕樣式為例)QT
- ASP.NET 2.0中XSLT的使用ASP.NET
- 作為一個新人,怎樣學習嵌入式LinuxLinux
- HTML標籤樣式初始化HTML
- 好程式設計師HTML5培訓教程-html和css的使用方法以及樣式程式設計師HTMLCSS
- C#的Replace函式,使用函式作為的引數需要注意C#函式
- javascript如何獲取內聯樣式和外部樣式表的值JavaScript
- CSS樣式表繼承CSS繼承
- 面試登記表樣式面試
- qt樣式表未生效QT
- SQL Server將直接傳遞查詢作為表使用的方法SQLServer
- XML文件不能使用css樣式表怎麼辦XMLCSS
- vue v-for中key的作用,使用index作為key會怎麼樣?VueIndex
- jquery給元素新增樣式表的方法jQuery
- 用html+css樣式,畫背景圖HTMLCSS
- HTML-CSS顏色樣式(推薦)HTMLCSS
- HTML模板和CSS基準樣式(一)HTMLCSS