.html 文件使用 <script> 标记简单地加载已编码了的 JavaScript 代码。之后,第二个 <script> 标记将数组的长度写出到浏览器页面,如 所示。
图 1. simple.html 的输出好了!数据文件包含三本书,相应的 JavaScript 文件也包含三本书。它真的可以工作!
通过函数加载上述第一个示例很简单,而且在大多数情况下可以发挥其作用,但它存在一些问题。第一个问题是对于数据何时被加载没有任何提示。如果数据是像页面那样被静态加载的,这不成问题。但是如果页面动态创建了一个 <script> 标记来按需加载数据,那么就很有必要知道 <script> 标记是何时完成的。实现此功能的最好的方法是让编码了的数据调用一个 JavaScript 函数,而不是只设置数据。
这个概念很重要,所以我将花一些时间来介绍一下为什么您必须要通过动态生成的 <script> 标记来加载数据。页面加载后,从服务器获得数据是 Web 2.0 的核心功能。一种方法是使用 AJAX 机制通过到服务器的调用来加载 XML。然而,出于安全性的原因,AJAX 机制只限于从单一域获取数据。这在大多数情况下都没有问题,但有时,您可能需要 JavaScript 代码运行在他人的页面上(例如,Google Maps)。
在这种情况下从服务器获得数据的惟一方法是通过动态加载 <script> 标记。获悉 <script> 标记何时加载的最好的方法是让 <script> 标记返回的脚本调用函数而不是简单地加载数据。 显示了在函数调用中编码的数据。
清单 6. Function1.jsAddBooks( [ { id: 1, name: 'Code Generation in Action', first: 'Jack', last: 'Herrington', publisher: 'Manning' }, { id: 2, name: 'PHP Hacks', first: 'Jack', last: 'Herrington', publisher: 'O\'Reilly' }, { id: 3, name: 'Podcasting Hacks', first: 'Jack', last: 'Herrington', publisher: 'O\'Reilly' } ] );
给出了相应的 .html 文件。
清单 7. Function1.html<html> <head> <title>Function 1 JS loader</title> <script> var g_books = []; function AddBooks( books ) { g_books = books; } </script> <script src="function1.js"></script> <script src="drawbooks.js"></script> </head> <body> <script>drawbooks( g_books );</script> </body> </html>
稍后将详细介绍 drawbooks 函数。这里重要的是了解一下页面如何定义 AddBooks 函数,该函数随后会由 function1.js 文件中的脚本调用。该 AddBooks 函数负责处理数据。而且被调用的 AddBooks 函数会向页面指示 <script> 标记被正确加载,并已加载完成。
要创建 function1.js 文件,我只对样式表稍微做了一点修改,如 所示。
清单 8. function1.xsl 样式表<xsl:template match="/"> AddBooks( [ <xsl:for-each select="books/book"> <xsl:if test="position() > 1">,</xsl:if> { id: <xsl:value-of select="@id" />, name: '<xsl:value-of select="js:escape(title)" />', first: '<xsl:value-of select="js:escape(author/first)" />', last: '<xsl:value-of select="js:escape(author/last)" />', publisher: '<xsl:value-of select="js:escape( publisher )" />' }</xsl:for-each> ] ); </xsl:template>
这里,我调用了一个函数,而不是简单地设置一个变量。这就是我所做的惟一更改。
回到页面,我使用了 drawbooks 函数来构建书的表格,这样我就能够确认数据被正确编码和正确显示。此函数是在 drawbooks.js 内定义的,如 所示。
清单 9. Drawbooks.jsfunction drawbooks( books ) { var elTable = document.createElement( 'table' ); for( var b in books ) { var elTR = elTable.insertRow( -1 ); var elTD1 = elTR.insertCell( -1 ); elTD1.appendChild( document.createTextNode( books[b].id ) ); var elTD2 = elTR.insertCell( -1 ); elTD2.appendChild( document.createTextNode( books[b].name ) ); var elTD3 = elTR.insertCell( -1 ); elTD3.appendChild( document.createTextNode( books[b].first ) ); var elTD4 = elTR.insertCell( -1 ); elTD4.appendChild( document.createTextNode( books[b].last ) ); var elTD5 = elTR.insertCell( -1 ); elTD5.appendChild( document.createTextNode( books[b].publisher ) ); } document.body.appendChild( elTable ); }
这个简单函数创建了一个表格节点,然后循环访问书的列表并为每本书创建一行,为每个数据元素分配一个单元格。此页面上的代码的结果如 所示。
图 2. function1.html 的结果现在我就可以查看一下此页面的输出并确认来自原始 .xml 文件的一切均已被正确转换成 JavaScript 代码,且数据被发送到 AddData 函数并被正确添加到页面。
细化函数调用技术我很喜欢函数调用这一技术,但我并不赞同将所有图书数据都放入一个块中。另一种方式是为每条记录采用一个调用,如 所示。
清单 10. Function2.jsAddBook( { id: 1, name: 'Code Generation in Action', first: 'Jack', last: 'Herrington', publisher: 'Manning' } ); AddBook( { id: 2, name: 'PHP Hacks', first: 'Jack', last: 'Herrington', publisher: 'O\'Reilly' } ); ...
对 .html 页面只需做少许修改,如 所示。
清单 11. Function2.html... <script> var g_books = []; function AddBook( book ) { g_books.push( book ); } </script> ...
这里更改了 XSLT,以使函数调用驻留在 for-each 循环体内。 显示了更新后的样式表。
清单 12. function2.xsl... <xsl:template match="/"> <xsl:for-each select="books/book"> AddBook( { id: <xsl:value-of select="@id" />, name: '<xsl:value-of select="js:escape(title)" />', first: '<xsl:value-of select="js:escape(author/first)" />', last: '<xsl:value-of select="js:escape(author/last)" />', publisher: '<xsl:value-of select="js:escape( publisher )" />' } );</xsl:for-each> </xsl:template> ...
对这个给定示例来说,这种更改看起来有些随意。但如果原始的 XML 数据集有多种数据类型,要为每种类型分配一个单独的函数调用会使 XSL 和页面上的 JavaScript 代码更为简单、更易于维护。
编码对象对小的页面来讲,使用 JavaScript 函数没有问题。但对于大型项目,就需要使用 JavaScript 语言的一些面向对象特性。是的,JavaScript 语言可以处理对象而且可以处理得很好。
显示了如何创建带有数据的对象。
清单 13. Object1.js