DHTML Scriptlet
----Web中的可重用对象
随着在Web中引入脚本语言,Internet已经转变成为支持复杂编程的动态环境,特别是IE4中动态HTML(Dynamic HTML)的引入,标志着Web正在走向成熟,已经不仅仅只是发布、浏览HTML页面,也可以作为应用程序用户界面的框架。而这不仅对于Web程序员,而且对于任何Windows应用程序的开发者来说,都是一个“利好”消息,至少在开发软件时又多了一个选择。
我们知道,用Visual Basic写程序非常容易,由于有大量的控件(Control)可以利用,用Visual Basic写程序就变成了一个组装控件并使其协同工作的过程。作为此点的自然延伸,微软公司在提出动态HTML(DHTML),使HTML具备足够的描述能力以后,又提出了Web组件(Web Component)的概念,而DHTML Scriptlet即为Web组件的一部分。
DHTML Scriptlet是一个封装了HTML和脚本(Script)的Web组件,它可以充分发挥DHTML的功能,DHTML Scriptlet使Web作者可以创建交互的Web内容,但它最主要的优势是它的可重用性,就像Windows的许多控件一样,你可以将设计好的DHTML Scriptlet重用于其他的Web页面或应用程序中。DHTML Scriptlet是将组件编程的好处和动态HTML及脚本结合在一起的一种方式。
IE4 4.0是微软公司从发行Visual Basic开发系统就开始的范式转移(paradigm shift)的一个例子。Visual Basic的强大功能不在于语言,而在于用Visual Basic将组件集成在一起的能力。IE4在这一点上又超出了Visual Basic,它不仅将组件集成在了一起,更加上网络和Windows环境。所以,在这个意义上,IE4是COM范式的一个自然进化:重用组件,而不是库。
在一个Web站点的建设中,通常会有很多相同或相似的Web页面成分(HTML成分或脚本成分),做重复工作是一件非常令人讨厌的事情,一般情况下,总是尽可能地重用这些页面成分。而为了重用这些页面成分,Web作者经常是剪切、粘贴,可能还要加以适当的修改。而用DHTML Scriptlet,Web作者只需要将这些Web页面成分封装为一个Scriptlet,定义好需要呈现(expose)给用户的属性和方法,那么,其他的Web页面或应用程序就可以重用这些定义好的DHTML Scriptlet了。通过Scriptlet呈现的属性和方法,其他Web作者不需要理解实现细节,就可以根据自己的需要定制这些Scriptlet。
在Web页面中使用DHTML Scriptlet,还可以加快页面的下载速度。因为DHTML Scriptlet只包含HTML和脚本,与ActiveX控件和Java Applet比较起来,是非常轻的,所以能够很快下载并运行。另外,DHTML Scriptlet下载到客户端后,就会缓存起来,这样,下次再浏览到包含该DHTML Scriptlet的页面时,就可以不用从网上下载,而直接从本机缓存中调用,从而加快了页面的下载速度。
如何在Web页面中使用DHTML Scriptlet
要使用Scriptlet,需要在Web页面中加入OBJECT标记,以标准URL的形式命名要引用的Scriptlet。为了标识DHTML Scriptlet,IE4引入了一种新的MIME类型:text/x-scriptlet。IE4就是根据“text/x-scriptlet”的MIME类型识别嵌入在Web页面中的DHTML Scriptlet的。下面的例子表示了如何在Web页面中嵌入DHTML Scriptlet,注意在OBJECT标记中并没有CLSID。
例子:
<OBJECT width=200 height=123
TYPE=”text/x-scriptlet” DATA=”Calendar.htm”>
</OBJECT>
其中,TYPE属性表示该OBJECT标识一个DHTML Scriptlet,DATA属性表示该Scriptlet的URL。
所有平台(Windows、Macintosh、UNIX)的IE4均支持这种MIME类型。
DHTML Scriptlet可以用任何脚本语言(如Microsoft JScript或Visual Basic Scripting Edition)来写。
DHTML Scriptlet的实现
前面我们已经说过,任何平台的IE4都支持DHTML Scriptlet,但平台不同,DHTML Scriptlet的实现方式可能也会不一样,在IE4支持的所有平台,一个DHTML Scriptlet就是一个Web页面。在Win32?平台,DHTML Scriptlet的实现充分利用了IE4的特性,以COM对象的方式实现DHTML Scriptlet,也就是说,一个DHTML Scriptlet就是一个COM对象,并且可以用于支持COM技术的其它应用程序中。
由于COM对象封装了IE4中的HTML绘制引擎(HTML Rendering Engine,这是IE4中的一个组件,文件名为MSHTML.DLL,它负责分析、显示HTML页面、COM控件以及Java Applet等)并且隐藏了它的大部分功能,所以,与将绘制引擎呈现给其用户的Web控件对象不同,Scriptlet组件只向其用户(包含该Scriptlet的Web页面或其它容器)呈现该Scriptlet的接口。这种呈现是通过将该Scriptlet(Scriptlet即是一个HTML页面)装入IE4的绘制引擎并以通常方式运行页面的脚本而实现的。一旦装入完成后,Scriptlet组件就准备与其容器(Container)进行交互。在通常的Web环境下,容器当然是指IE4,但任何容器,包括用Visual Basic写的程序,或甚至Microsoft Word,都可以像插入任何其它ActiveX控件一样地插入DHTML Scriptlet。
然后Scriptlet组件桥接层就作为一个代理(Broker)起作用,将对属性和方法的请求传送给Scriptlet,而将事件从Scriptlet中传出。桥接层也完成一些薄记功能如在容器和绘制引擎之间协商Scriptlet的尺寸。
DHTML Scriptlet的安全性
Scriptlet的安全性与DHTML和Script本身一样,并且Scriptlet会识别出在其被放入一个如IE4这样的安全容器中时,遵守容器的安全策略。
一般情况下,为了能够正确地操作,Scriptlet必须从其容器页面所在的Web服务器中装入,就像Java Applet一样。而且IE4的安全级别要设置为中(Medium)或低(Low)。
如何写DHTML Scriptlet
Scriptlet可以是HTML页面或ASP页面,你可以使用任何HTML著作工具(如Microsoft FrontPage或Visual InterDev)创建Scriptlet。当然,如果你的环境不支持脚本编程,你就要在设计好HTML页面后再手工加入脚本代码。
Scriptlet只呈现全局变量、过程及函数。要在需要呈现给外部的变量或函数名之前加一个“public_”前缀,任何带有这个前缀的全局变量都成为了Scriptlet的可读写的属性,任何带有这个前缀的全局函数或过程都成为了Scriptlet的公共方法。但若你在Scriptlet之外引用这些属性或方法,则不需要这个前缀。例如,在Scriptlet中做如下声明:
<Script Language=”JScript”>
public_property1 = ‘some text’;
function public_method1(param1, param2)
{
… ‘some code’…
}
</Script>
则可以在包含该页面的脚本中引用这些属性或方法:
Scriptlet1.property1 = ‘some different text’;
A = Scriptlet1.method1(2, ‘sill more-text’);
事件
Scriptlet可以引发两种事件:onscriptletevent事件和标准窗口事件(如鼠标点击),标准窗口事件不能由Scriptlet随意产生,只能在某些情况下引发。
Scriptlet可以认为本质上比传统的Windows程序更动态一些,对于在装入并分析过该Scriptlet以后才可知道的事件类型,Web著作工具并不能理解。基于此,Scriptlet与其容器之间的通信只能通过一种事件类型:onscriptletevent。Onscriptletevent事件包含两个参数:一个字符串及一个对象。事件处理程序可以根据传送的字符串决定如何响应此事件,对象参数则包含有关事件本身的一些信息。
Scriptlet支持的标准窗口事件是通过传播产生于Scriptlet中的事件而引发的。如果你在Scriptlet中写了一个事件处理程序,来捕捉鼠标点击事件,则在事件处理程序中就可以调用bubbleEvent方法将该事件传播到Scriptlet的容器中,就像是从Scriptlet容器中产生的鼠标点击事件一样。
例子:
<INPUT TYPE=”BUTTON” onclick=”doClick()”>
<Script Language=”JavaScript”>
Function doClick()
{
// 将鼠标点击事件传播给容器
window.external.bubbleEvent();
}
</Script>
上下文菜单
可以很容易地为Scriptlet创建上下文菜单。这是一个弹出式菜单,将鼠标移到Scriptlet组件上并按右键,就会弹出此菜单。可以为每一个Scriptlet都定义一个上下文菜单,也可以在任何时候替换(修改)该菜单。
要创建一个上下文菜单,首先建立一个字符串数组,将数组元素每两个一组配对,其值为字符串:每对的第一个元素是将出现在上下文菜单中的标号字符串(Label String),即菜单项名称,第二个元素是当该菜单项被选中时所要执行的Scriptlet中的脚本函数名。数组初始化完成后,以该数组作为唯一参数调用window.external.setContextMenu函数。这样,当用户在组件上按鼠标右键时,将弹出上下文菜单。
例子
<Script Language=”VBScript”>
sub window_onload
dim a(6)
a(0) = “Add &Hello”
a(1) = “Hello”
a(2) = “Add &Goodbye”
a(3) = “Goodbye”
a(4) = “&About”
a(5) = “About”
window.external.setContextMenu(a)
end sub
</Script>
高级特性
属性的行为在有些情况下与内存地址一样,但只是这样还不够,设想一个组件有一个颜色属性,在设置该属性后,希望能够立即在组件上反映出来,大多数组件都需要在改变它们的一些属性时能够立即反映出来。为了支持这一点,组件体系结构允许你将函数定义为组件的属性。将函数定义为属性是在函数名前加public_get_或public_put_前缀。
例子
下面的例子显示了函数如何用于实现属性:
<Script Language=”Jscript”>
property1 = ‘some text’;
property1GetCount = 0;
property1PutCount = 0;
function public_get_property1()
{
property1GetCount++; // 跟踪该函数的调用次数
Return property1; // 返回实际的属性值
}
function public_put_property1()
{
property1PutCount++;
property1 = new_value;
refresh();
}
</Script>
在这个例子中,函数记录了自己被调用的次数,在put函数中一旦设置了新值,组件将以某种方式刷新,你仍然可以在容器页面中像其它属性一样引用Scriptlet的property1属性:
Scriptlet1.property1 = ‘new text’;
A = Scriptlet1.property1;
在第一行中,给属性property1赋值导致public_put_property1函数被调用以存储属性值(“new text”字符串),第二行调用public_get_property1函数以取得property1属性的值。
如果是用JScript写Scriptlet,则还有另外一个选择来描述Scriptlet的界面,即使用public_description。如果用public_description定义一个对象,则该对象的成员将成为外部可以引用的属性和方法,而不再需要用public_前缀。如果有带有public_前缀的全局变量或函数,则被忽略。
目前版本的VBScript不允许像JScript那样用public_description创建对象,但将来的版本将可以。
用什么机制来定义Scriptlet的界面呢?这在很大程度上是一个风格的问题。用public_description来描述Scriptlet的公共界面,由于集中在一起,因而比较简洁,而不是像用public_前缀那样,界面的定义散落在整个Scriptlet代码中。
一个DHTML Scriptlet实例
下面给出一个DHTML Scriptlet实例,该Scriptlet实现了文本的动态显示,可以用来显示新闻。