了解了 XML 在 Firefox 浏览器中的基本显示和样式后,下一个要关注的功能就是脚本。本文中,我将展示利用 JavaScript 代码处理 XML 这一基本概念。本文包含的所有代码示例和屏幕截图都是在 Ubuntu Linux® 系统中使用 Firefox 1.5.0.4 创建和测试的,配置文件没有修改过(即没有扩展,保留了安装时的默认选项)。如果要编写用于 XML 处理的跨浏览器代码,可能必须使用其他的浏览器嗅探技术,但是,我没有在本文介绍这些技术。
您可以使用 Web 页面内嵌的 JavaScript 代码加载 XML 文档。我将从一个 HTML Web 页面示例入手,该页面加载一个简单的 XML 邮件列表格式用于动态更新,要加载的 XML 文档如 清单 1 所示(labels.xml)。
清单 1.(labels.xml)地址标签 XML
<?xml version="1.0" encoding="iso-8859-1"?> <labels> <label id='ep' added="2003-06-10"> <name>Ezra Pound</name> <address> <street>45 Usura Place</street> <city>Hailey</city> <province>ID</province> </address> </label> <label id='tse' added="2003-06-20"> <name>Thomas Eliot</name> <address> <street>3 Prufrock Lane</street> <city>Stamford</city> <province>CT</province> </address> </label> <label id="lh" added="2004-11-01"> <name>Langston Hughes</name> <address> <street>10 Bridge Tunnel</street> <city>Harlem</city> <province>NY</province> </address> </label> <label id="co" added="2004-11-15"> <name>Christopher Okigbo</name> <address> <street>7 Heaven's Gate</street> <city>Idoto</city> <province>Anambra</province> </address> </label> </labels> |
清单 2 是仅包括一个链接的 HTML 页面,链接显示 “Click here to load addresses”。单击链接,地址标签的信息被添加到页面中。
清单 2. HTML 页面利用 JavaScript 加载 XML 用于动态更新
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type"> <title>Address book</title> <script type="application/javascript"> var ELEMENT_NODE = 1 //TEXT_NODE function loadAddresses() { xmlDoc = document.implementation.createDocument("", "", null); xmlDoc.onload = writeList; xmlDoc.load("labels.xml"); } function writeList() { var labels = xmlDoc.getElementsByTagName('label'); var ol = document.createElement('OL'); for (i=0; i < labels.length; i++) { var li = document.createElement('LI'); for (j=0; j < labels[i].childNodes.length; j++) { if (labels[i].childNodes[j].nodeType != ELEMENT_NODE) continue; var cdata = document.createTextNode( labels[i].childNodes[j].firstChild.nodeValue); li.appendChild(cdata); } var labelId = document.createTextNode('(' + labels[i].getAttribute('id') + ')'); li.appendChild(labelId); ol.appendChild(li); } document.getElementById('updateTarget').appendChild(ol); } </script> </head> <body id='updateTarget'> <p> <a href="javascript:loadAddresses()">Click here to load addresses</a> </p> </body> </html> |
script
元素体现动态特性,定义一个 JavaScript 函数 loadAddresses
,这个函数将被 HTML 中的链接调用。该函数创建一个空文档实例,然后使用 load
函数读入 清单 1(labels.xml)。load
函数是异步执行的,因此,在 XML 文档读入的同时,脚本可跳到下一行执行,使您能够在 XML 加载开始后就使用一个触发函数开始运行。因此,我为一个独立的函数 writeList
设置 onload
属性。该函数使用方便的文档对象模型(Document Object Model, DOM)的 getElementsByTagName
方法遍历标签。如果 XML 文档使用名称空间,那么要使用 getElementsByTagNameNS
表单而不是上面的方法,并将名称空间指定为第一个参数。在下一节中,您将会看到一个这样的例子。在 清单 2 中,只使用 DOM 的基本层(叫做 DOM Level 1)进行 XML 处理。对于支持名称空间的应用程序,需要使用 DOM Level 2,它扩展了许多 Level 1 方法,可以支持名称空间。清单 2 创建了一个表示有序列表的子树,将 HTML 主文档作为工厂(factory)来创建节点。这样,生成的子树可以插入到 HTML 主文档中。清单 2 使用普通模式读取源 XML 树,然后将相应的节点添加到输出 HTML 子树中。
对每个 label
元素执行循环语句 labels[i].childNodes
,查找 name
和 address
子节点。为避免对文本节点的子节点执行操作,使用 nodeType
测试。使用 firstChild.nodeValue
方法进行访问获得 name
元素的子文本。对 address
元素来说,第一个子节点是空格。您不能访问 address
的子节点的任何文本内容。使用 getAttribute
方法可以访问 ID。将收集到的所有文本添加到列表项中。编译完所有的列表项元素之后,使用 appendChild
方法更新包含子树的 HTML 文档。可以使用 updateTarget
ID 标记将添加该子树的元素(body
)。当第一次在 Firefox 中加载该 HTML 时,只能看到如 图 1 所示的链接。
图 1. 加载清单 2 之后的浏览器显示
单击链接,就立刻获得最新的显示,如 图 2 所示。
图2. 加载清单 2 并单击链接之后的浏览器显示
在本系列的第一部分中,我介绍了如何在 Firefox 中浏览 XML 文件。要在这种情况下使用 JavaScript,需要在脚本文件中嵌套一个引用。清单 3(designers.xml)显示的示例 XML 文件是一个设计师列表。XML 引用独立的 CSS 文件来显示 XML,引用独立的 JavaScript 文件来创建链接元素。
清单 3.(designers.xml)表示时装设计师链接的 XML 格式
<?xml version='1.0' encoding='utf-8'?> <?xml-stylesheet type="text/css" href="designers.css"?> <designers> <blurb> <designer homepage="http://doria.co.uk">Doria Ltd.</designer> of London </blurb> <blurb> <designer homepage="http://samsara.biz">Samsara Saris</designer> of Mumbai </blurb> <blurb> <designer homepage="http://pcp.co.uk">Pikeman Camouflage, Plc.</designer> of London </blurb> <blurb> <designer homepage="http://mandalay.co.jp">Mandalay</designer> of Tokyo </blurb> <xhtml:script xmlns:xhtml="http://www.w3.org/1999/xhtml" src="designers.js" type="application/javascript"/> </designers> |
样式表处理指令提供显示 XML 的基本指令。清单 4(designers.css)显示 CSS。
清单 4.(designers.css)修饰清单 3 中 XML 格式基本显示的 CSS
* { display: inherit; } designers { display: block; } blurb { margin: 1em; width: 20em; } a { display: inline; text-decoration: none; color: green; border: thin blue solid; } script { display: none; } |
样式表通知浏览器把 designers
和 blurb
作为块区域,而忽略用于显示的 script
元素。通过将链接(a
元素)和可见的提示一同显示,可以更容易识别出链接。您将注意到在 XML 源文件中不存在 a
元素。因此,这里要用到脚本。JavaScript 代码使用 XHTML 链接元素代替 designer
元素。XHTML 链接元素通过一个 XHTML 脚本元素提供,因此只要使用到 Firefox,就可以嵌入任何 XML(当然,您可能会遇到模式兼容性的问题)。Firefox 在载入的文档中遇到脚本后就运行它们,因此要把脚本放在所有待处理的元素之后。清单 5(designers.js)显示了 JavaScript 代码。
清单 5.(designers.js)将 XML 元素从清单 3 转换为 XHTML 链接的脚本
//Save the XHTML namespace for when you need it var xhtmlns = "http://www.w3.org/1999/xhtml"; //get all elements named "blurb" in the document //The first "" indicates no namespace for the element we're seeking var blurbs = document.getElementsByTagNameNS("", "blurb"); //Loop over each element we found for (var i=0; i < blurbs.length; i++) { //retrieve the blurb element from the collection var blurb = blurbs[i]; //Get the designer element within the blurb //Assumes only one designer element per blurb var designer = blurb.getElementsByTagNameNS("", "designer").item(0); //In DOM the text in designer is actually a text node object child //of blurb. The link title is the value of this text node //Assumes the text node is normalized var link_title = designer.firstChild.nodeValue; //Link URL is the homepage attribute of designer, in no namespace var link_url = designer.getAttributeNS("", "homepage"); //Create a new XHTML namespace link element var xhtml_link = document.createElementNS(xhtmlns, "a"); //Create a new text node with the link title var new_text_node = document.createTextNode(link_title); //Set the href attribute to the link URL xhtml_link.setAttributeNS("", "href", link_url); //Attach the text node with the link title to the XHTML link xhtml_link.appendChild(new_text_node); //Replace the designer element with the new XHTML link element blurb.replaceChild(xhtml_link, designer); } |
因为 清单 5 中的脚本可以支持名称空间(尤其是其中的 XHTML 名称空间),所以我使用支持名称空间的 DOM 方法。图 3 显示查看 清单 3 的结果。可以看到,浏览器立即应用 CSS 和脚本。
图 3. 加载清单 3 的浏览器显示
|
使用 JavaScript 可以访问大多数的浏览器功能,其中包括 XSLT 引擎。清单 6 是一个执行 XSLT 转换的脚本片段。
清单 6. 加载 XML 文档和 XSLT 转换并执行转换的 JavaScript 代码
//Create an XSLT processor instance var processor = new XSLTProcessor(); //Create an empty XML document for the XSLT transform var transform = document.implementation.createDocument("", "", null); //Load the XSLT transform.onload = loadTransform; transform.load("display.xslt"); //Triggered once the XSLT document is loaded function loadTransform(){ //Attach the transform to the processor processor.importStylesheet(transform); source = document.implementation.createDocument("", "", null); source.load("source.xml"); source.onload = runTransform; } //Triggered once the source document is loaded function runTransform(){ //Run the transform, creating a fragment output subtree that //can be inserted back into the main page document object (given //in the second argument) var frag = processor.transformToFragment(source, document); //insert the result subtree into the document, using the target element ID document.getElementById('updateTarget').appendChild(frag); } |
如果想创建整个输出文档,而不是创建插入到其他文档的子树,使用 transformToDocument
方法代替 transformToFragment
方法。
|
首先是一份重要的免责声明:XML 的 Web 脚本语言还没有完全标准化。文中涉及的很多内容并不是跨浏览器标准化的。由于本系列主要关注 Firefox,文中没有介绍其他浏览器需要做的修改,但是您可以继续了解如何修改以适合各种用户界面。XML 甚至是 XSLT 的 JavaScript 操作都可使用跨浏览器库。如果不能专门针对基于 Mozila 的浏览器进行开发,那么可以使用跨浏览器库。处理 Web 脚本时,必须要考虑可访问性。同时,要注意内容、处理和显示的分离,避免在最后一刻才将引用嵌入到脚本中。另外,也可以保存和管理不包含这类引用的 XML,在将 XML 传送给浏览器之前,插入这些引用。
现在,您已经了解如何在 Web 脚本中加载和处理一个独立的 XML 文件,如何从 XML 主文档中调用脚本,以及如何从脚本中调用 XSLT 处理器。因为 JavaScript 支持所有浏览器特性,因此可以使用它进行更多 XML 处理。可以将学过的各种脚本编制技术,例如动态 HTML(DHTML),应用到 XML 处理中。ECMAScript for XML (E4X) 是一组对 JavaScript 的语言增强(从技术上讲,是 ECMAScript),使 JavaScript 的 XML 处理更容易。这个新标准为 XML 处理添加了专门的用法。Firefox 1.5 支持 E4X,我将在以后的文章中对它进行介绍。不用牺牲 XML 的强大的结构特性,适度和精心设计的脚本可以展现现代 Web 应用程序的全部魅力。
学习
- 您可以参阅本文在 developerWorks 全球网站上的 英文原文。
- Firefox 1.5 中的 XML,第 1 部分: XML 特性概述(2005 年 9月)和 Firefox 1.5 中的 XML,第 2 部分: 基本 XML 处理(2006 年 3 月):回顾该 developWorks 系列的前两篇文章。
- 了解 CSS 和使用 CSS 显示 XML 的基本概念。查看这些教程:
- 级联样式表显示 XML:使用级联样式表显示 XML,第 1 部分:在 Web 浏览器中显示 XML 的基本技术(2004 年 11 月)。
- 然后是 级联样式表显示 XML:使用级联样式表显示 XML,第 2 部分:在 Web 浏览器中显示 XML 高级技术(2005 年 2 月)介绍在浏览器中使用 CSS 修饰 XML 的高级主题,包括脚本技术的讨论。
- 如果您熟悉 XSLT,可以直接查看 级联样式表显示 XML:使用级联样式表显示 XML,第 3 部分:XSLT 与 CSS 结合处理 XML(2005 年 6 月)讨论对 XML 源应用 XSLT,包括使用 XSLT 技术处理 CSS 获得 HTML 或 XML 输出。
- JavaScript 和 DOM:将这些方便的 Mozilla 参考资料添加为书签。
- Javascript MIME 类型:通过 Anne van Kesteren 的文章了解选择 JavaScript 媒介类型的复杂性。
- IBM XML 1.1 认证: 了解如何才能成为一名 IBM 认证的 XML 1.1 和相关技术的开发人员。
- XML:访问 developerWorks XML 专区,获得广泛的技术文章和技巧、教程、标准和 IBM 红皮书。
- developerWorks 技术活动和网络广播:随时关注技术的最新进展。
获得产品和技术
- Firefox:获得基于 Mozilla 的 Web 浏览器,该浏览器提供标准遵从性、高性能和安全性,以及稳定的 XML 特性。当前版本为 1.5.0.4。
讨论
- XML 专区讨论论坛:参与和 XML 相关的讨论。
Uche Ogbuji 是 Fourthought Inc. 的顾问和创立者之一,这是一家专门从事企业知识管理 XML 解决方案的软件供应商和咨询公司。Fourthought 开发了 4Suite,这是一个用于 XML、RDF 和知识管理应用程序的开放源码平台。Ogbuji 先生还是 Versa RDF 查询语言的主要开发人员。他是一位出生在尼日利亚的计算机工程师和作家,目前在美国科罗拉多州博耳得定居和工作。您可以通过他的 Weblog Copia 进一步了解 Ogbuji 先生。 |