cutting@microsoft.com Download the code (58KB) |
Dino Esposito |
Once You Had DHTML Scriptlets
In the January 1998 installment of Cutting Edge, I examined DHTML scriptlets. The idea behind them is both simple and clever. A DHTML scriptlet is an HTML page with all of its typical content (tags, text, and script code) embedded inside a host page. In other words, a scriptlet is an HTML-based COM component. By combining scripting code and HTML tags, you can define a certain component with a certain behavior and a certain set of properties, methods, and events. In this scenario, the DHTML object model plays a primary role in bringing the scriptlet's user interface to life. Having a scriptlet inside a page is conceptually like running another instance of Internet Explorer 4.x.
Although DHTML scriptlets were introduced primarily to componentize pieces of DHTML code, they are not necessarily required to have a user interface. You can easily write hidden scriptlets that simply provide a particular behavior and work like a COM server. DHTML scriptlets are hosted inside a page through the <OBJECT> tag and internally follow the HTML syntax by using a specific public descriptor object to define the programming interface. More information about and examples of scriptlets can be found in my book, Instant DHTML Scriptlets (Wrox Press, 1998).
XML Scriptlets
If you need Web components with a user interface, then DHTML scriptlets provide your most lightweight choice. However, the browser is just one possible container for script code. More recently, WSH (see Cutting Edge in the June 1998 issue of MIND) has become a new and really useful script-based environment. Furthermore, the release of the Microsoft Script Control (see http://msdn.microsoft.com/scripting) makes it easy for each developer and each application to host a scripting engine. The demand for reusable, self-contained script code components is continually growing.
DHTML scriptlets are an interesting solution, but they're too closely oriented to the Web for some applications. Server scriptlets (which I covered in the May 1998 issue) provide a more general layout that encompasses DHTML scriptlets as a special case. Server scriptlets are written in XML codein fact, they've been called XML scriptlets, or simply scriptlets and they have a few significant differences from DHTML scriptlets. First of all, XML scriptlets have a runtime engine that makes them appear to be full-fledged automation components. Second, they can't have a user interface or fire events. Scriptlets are just servers, though they may run on either the client or server side of the Web.
On the client side you can use scriptlets through the
<OBJECT> tag and the CLASSID attribute as if they were standard ActiveX® controls. On the server side, you can use them as UI-less COM servers whose instances are created in ASP code through the VBScript CreateObject or JScript ActiveXObject functions.
The great advantage of XML scriptlets is that they can be used without significant modification in conjunction with WSH. Although their interface is COM-based, you don't need a clear perception of the COM stuff that is behind it. You just write a few JScript or VBScript functions, give them a descriptive global name (a ProgID), a 128-bit identification number (a CLSID), and add the component to the system registry using the regsvr32.exe utility.
OK, much of this is actually COM-related stuff. But if you don't use terms like ProgID, CLSID, or registration, you won't be reminded of COM since interface definitions, the implementation, class factories, IUnknown, and the like are isolated in a virtually invisible runtime module.
In addition to the references I already mentioned, check out the series of articles written for MSDN by Andrew Clinick (links at the end of this article) for a really clear grasp of the evolution of scriptlets.
Scriptlet Events
Like most development concepts, scriptlets have both an upside and a downside. They are lightweight, easy-to-write objects, but lack some code protection. Moreover, both DHTML and XML scriptlets have some structural drawbacks. DHTML scriptlets can have a user interface, but rely on the browserand the browser must be Internet Explorer 4.0 or later. XML scriptlets are hidden objects that work in the background and don't accept even the most innocent MsgBox call.
Another scriptlet difficulty is event handling, both when firing events and listening for them. XML scriptlets don't support event handling yet because they rely on special modules called interface handlers to implement all the required COM interfaces (I discussed this in the May 1998 issue). At present, only the IDispatch interface has been defined to handle automation. Handling events requires additional interfaces, starting with IConnectionPointer.
DHTML scriptlets can raise events in two ways. They can bubble standard events like onclick or onmouseover, or they can raise custom events. When bubbling standard events, scriptlets make use of the window.external.bubbleEvent method. Raising a custom event involves using the onscriptletevent event, whose first argument is the actual name of the event.
The problem is that scriptlets can't easily communicate with each other on the same page. Suppose you want to link two scriptlets so that one performs some action in response to an event fired by the other one. A typical example would be an edit box component that notifies a label when its text changes. If you want a page with an edit control and a label that
displays the length of the text, you might want to turn the edit control into a scriptlet that contains the actual UI element, and catch the change event it raises through onscriptletevent. Next, you can explicitly invoke a method on the label scriptlet to make it redraw properly. You have to write glue code on the main page that hosts both components. But can the scriptlets do it all by themselves? In other words, can a given scriptlet listen for the events raised by another scriptlet?
The onscriptletevent interface is not enough because it assumes you know the name of the object that raises it. You want something different here: a scriptlet that's ready to handle any event raised by any scriptlet in the same page, regardless of the names of the various scriptlets involved.
If you're familiar with Java, you'll realize that what I'm
thinking about is similar to the Java delegation model for event handling.
By design, DHTML scriptlets have a simplified event-raising mechanism. They always raise the same event function, leaving specific information about the real event in the function's arguments. But there's a better way to do this. In practice, you define and document the event interface your scriptlet exposes. An event interface is merely a collection of functions, each of which is called as a consequence of a certain event that happens inside the scriptlet. For example,
the edit box discussed previously might have the following event interface:
OnTextChange( newText )
OnKeyPress( key )
OnMouseClick( position )
The first function will be called each time the edit buffer changes, while the other two will occur when you press a key or click on the component.
Figure 1: Scriptlet Interaction |
The HotImage scriptlet exposes an interface called Clicked, which represents a single function Clicked that takes no parameters. This event is fired when the button is clicked. The four scriptlets in this system can register themselves as listeners of this event by simply adding a Clicked function to their own interfaces.
The complete code for a listener scriptlet is shown in Figure 2. The lines you need to change to support event listening are bracketed by lines of slashes.
The most interesting part of this story is how a scriptlet can retrieve a connection point from all the scriptlets found on the host page. Figure 3 shows the source code for the HotImage control. The core functionality is contained in the following snippet:
The function EnumScriptlets gets called whenever the user clicks on the control, before the standard onclick event is bubbled down. It obtains a reference to the document object of the parent page. Then it walks the entire collection of
<OBJECT> tags, checking the TYPE attribute against the MIME string for scriptlets: text/x-scriptlet.
For each scriptlet found the function checks the validity of the expression given by the object name and the function to call. In EnumScriptlets, it is obj.Clicked.
The typeof function returns the word "function" if it is defined or "undefined" if it's not. This lets you issue a call only if the method actually exists. The previous fragment also shows another feature: a scriptlet can expose more than one interface. In the previous sample, I've exposed both XClicked and XClicked2. (Since these are not exactly real COM interfaces, I chose to replace the typical I in the name with an X.) |
Figure 5: Listening for Xclicked |
Example Online! |
The source code for the host page is shown in Figure 4. Each scriptlet that fires events ends up calling methods exposed by other scriptlets on the same host page. Figure 5 shows the sample page where scriptlets 1 and 4 are listening for XClicked, and scriptlets 2 and 3 are listening for XClicked2. The red rectangles are automatically painted by clicking the first button. You could further refine this approach by introducing a specific function to toggle the scriptlets' listening capability when desired.
Script Components
|
Figure 6: Internet Client SDK Page |
You should be familiar with the autohide feature of the Windows taskbar and the hierarchical menu look of the Internet Client SDK documentation (see Figure 6). Why not try to put them together? To create such a component, you would first arrange a panel with a little button that hides or displays it. Then the content of the panel should render a tree-based menu with expandable main items and child items that cause some action to occurtypically displaying a new page in another frame.
|
Figure 7: Show/Hide Panel Navigation |
Figure 7 shows an example of what I mean. The panel on the left is a scriptlet that contains a header bitmap plus a hierarchical menu. By clicking the button in the top-right corner, you can hide or show the panel (see Figure 8). The code to get this result is shown in Figure 9.
|
Figure 8: Hidden Panel |
Example Online! |
The HideOff scriptlet is an HTML page composed of a table with a single row and two columns. The first column contains the menu, while the second shows the button that opens and closes it. The body of the page is contained in two mutually exclusive DIV sections, one for the panel's hidden state and one for its displayed state.
The panel contains a header and a menu bar. The header can contain literally anything in HTML format. For example, you can use a bitmap like this:
The header content corresponds to the settable Head property exposed by the scriptlet. The menu is created dynamically through the following properties and methods:
With AddItem you can add a new main menu node. The method returns an ID you'll use to add specific items to it. Here's an example:
Calling AddItem with an empty string inserts a separator line. I think that the trickiest part of this code is event handling. In order to handle highlighting and clicking, you should detect any mouse movement over any item (see Figure 10). The scriptlet interface can be customized via SetColors and SetStyle, which let you set the background, normal, and highlighted colors, the font family, and size.
|
Figure 10: Detecting Mouse Movement |
It's easy to integrate your component with existing pages, even richly featured ones. Refer to Figure 11 for the source code of the sample page shown earlier.
Is Readability a Real Issue?
and adding a bit of initialization code to set up the various menu items, as shown in Figure 11.
Scriptlets are a great step forward. I think that many developers underestimate their potential because they don't like to be tied to Internet Explorer 4.0 as their browser. However, scriptlets are helpful for both developers and users. Making use of scriptlets slices away a lot of the complexity and code volume you need to provide DHTML effects. But you still have to insert a number of <SCRIPT> tags to take care of component initialization and event handling, as you can see in Figure 9.
Behaviors and Dynamic Properties
This snippet shows how a behavior works. In a nutshell, you attach events and procedures to a given tag. What's important, however, is that the tag is hosted in the main page and not in an overlapped window as with DHTML scriptlets.
Technically speaking, a behavior is a new Cascading Style Sheet style that points to a URL where an XML scriptlet implements the logic. This type of scriptlet implements a bunch of new COM interfaces. You can write behaviors with low-level languages like C++ or with scripting languages like VBScript. In this case, your scriptlet will declare that it implements a specific interface handler called, appropriately,
behavior. (Before behaviors, the only interface handler
was automation.)
Behaviors are neither the only new feature of Internet Explorer 5.0 nor the coolest. From a script development standpoint, dynamic properties promise to be useful. They are expressions assigned to a traditional object's attributes. Dynamic properties are linked to dependency lists and get evaluated each time Internet Explorer 5.0 detects a change in some element of the list. A typical use of dynamic properties would be a table recalculation mechanism triggered by some content change. This means an HTML table will be a bit more comparable to a Microsoft Excel spreadsheet. If you want to know more about behaviors, dynamic properties, and other features of Internet Explorer 5.0, I recommend you take a look at "Internet Explorer 5.0: The Inside Story" (MIND, September 1998). In the near future, Cutting Edge will also be covering various topics specific to Internet Explorer 5.0.
JScript and VBScript 5.0
may be interpreted in two ways: as a comparison or as an assignment. Providing two functions lets the developer remove ambiguity in such cases. In other cases, the only difference is that Eval always returns a value, while Execute does not.
Version 5.0 of both engines is currently planned to ship with Internet Explorer 5.0. Since Internet Explorer 4.0 runs the 3.0 version of each language, version 4.0 of the languages seems to be missing. The documentation available as compiled HTML files on the Microsoft scripting site (http://msdn.microsoft.com/scripting) claims that the 4.0 language engine is part of Visual Studio 6.0. However, I was unable to find any significant difference between this and the 3.0 versions.
Summary
|
From the November 1998 issue of Microsoft Interactive Developer.