MSDN Magazine > Issues and Downloads > 2000 > December >  Cutting Edge: Element Behaviors in Internet Exp...
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
MSDN Magazine
Element Behaviors in Internet Explorer 5.5
Dino Esposito
Download the code for this article: Cutting1200.exe (62KB)
Browse the code for this article at Code Center: Element Behaviors

B
ehaviors were introduced with Microsoft® Internet Explorer 5.0 to provide easy separation of script from content. Before the advent of behaviors, DHTML scriptlets were the only way to encapsulate scripting code into manageable elements if you wanted to make your code more object oriented. For a primer on DHTML scriptlets, refer to the Cutting Edge column in the January 1998 issue of Microsoft Internet Developer. I also covered behaviors in the April 1999 issue.
      Although scriptlets allowed you to remove lots of code from your HTML page, the amount of script code in a scriptlet-enabled HTML page wasn't actually reduced very much. The code that scriptlets removed from the page and encapsulated into script components was soon replaced by other <SCRIPT> blocks, which were necessary to actually program those components and to make them handle events at the page level. There was no great benefit to this trade, and it broke cross-browser compatibility. (Though submitted to the W3C committee, scriptlets never became a W3C standard.)
      Although behaviors were designed to work around the limitations of scriptlets, they also have drawbacks. In this column, I'll follow up my May 2000 MSDN® Magazine Cutting Edge column, "Extending HTML with Custom Tags", outlining good uses for behaviors, their limitations, and alternative options. I'll also build on Dave Massy's excellent article, "Building Your Own Custom Elements for Internet Explorer 5.5," which originally appeared in the January/February 2000 issue of MSDN News.

How to Write Behaviors

      Before going any further, let's review a few basics about writing behaviors. A behavior can be written in two ways, each requiring different skills. One way is to do everything with script code, which results in HTML Component (HTC) files. Alternatively, you can create a binary DHTML behavior that is nothing more than a plain COM object.
      HTC files follow an XML syntax, with the root tag <PUBLIC:COMPONENT>. When writing a behavior, you usually follow these general steps:
  1. Declare the public interface.
  2. Hook up events.
  3. Read input parameters.
  4. Initialize the component.
  5. Build the subdocument.
  6. Insert the new HTML code.
      Behaviors are programmable components that expose a set of properties and methods and fire custom events as well. Thus, the first thing to do is publish the behavior's programming interface. The following line shows how to declare a property called height.
<PUBLIC:PROPERTY NAME="height" />
The name of the property can be used to assign a value from within the calling HTML page. From an HTML page, you use a behavior's property just like a tag attribute. Likewise, a behavior exposes methods that you can call from HTML script code.
      One of the key reasons for using a browser-specific solution like behaviors instead of more portable, plain script functions is that you can easily hook up page events, yet your code will be reusable and maintainable. To connect to and possibly handle a page event, hook an event and use an event handler. For example, you'd say:
<PUBLIC:ATTACH EVENT="onclick" onevent="DoClick" />
      The <PUBLIC:ATTACH> tag informs Internet Explorer that you want to sink the specified eventâ€"in this case, the onclick event. When the event gets fired (the item is clicked), the browser runs the function you indicated through the HANDLER attribute. This function must be reachable from the page, so you must either import it from an external file or get it from an embedded <SCRIPT> tag.
      At this point in the process you also need to read the values of the public properties and, when needed, initialize them with some default values.
if (height == null)
    height = "90%";
      In most cases, you write behaviors because you need to handle certain events in a particular way or because you need to inject a special subdocument in the body of the host document. In the former case, your goal is to hook up to the desired events and handle them properly. In other words, the code you write is invoked only when a certain event occurs. A typical example is when you want to automate a hyperlink, highlighting it when the mouse passes over it. Your behavior just needs to hook the onmouseover and onmouseout events and toggle a few Cascading Style Sheet (CSS) styles.
      An alternate scenario is when you want a behavior to act as an extended, highly specialized HTML tag. In this case, you need to insert some custom code into the body and, more importantly, the Document Object Model (DOM) of the current page. The initialization step, therefore, becomes extremely important.
      A typical novice error is handling the onload event just to get into the game without realizing that onload is not necessarily triggered only when the page has completely finished loading. You can't be certain about the readiness state of the DOM just because the onload event has fired. You really have to use the ondocumentready event instead.
<PUBLIC:ATTACH EVENT="ondocumentready" HANDLER="DoInit" />
      When you need to inject your code into the DOM, not handling the ondocumentready event is a programming error. However, this can be a subtle problem; it doesn't always result in obvious errors. For example, during the course of several months I had a few behaviors that worked just fine in a production environment, despite the fact that they were hooking the onload event and then inserting a subtree into the regular DOM. But it was merely luck that prevented problems. I happened to be using behaviors that had no properties, and a behavior without properties doesn't need to initialize anything. It just builds up an HTML string and inserts it at a certain point, so the readiness of the DOM was not important.
      It wasn't until I had a slightly different needâ€"writing a property-aware behaviorâ€"that I realized the significant difference between onload and ondocumentready. Consider the following snippet:
<dino:tabstrip height=150 label="Expoware Soft">
To be certain that both label and height are correctly set before you inject the behavior's information into the document, make sure you read them after the ondocumentready event has been raised, not before.
      Once the string representing the subdocument is ready, you just call the element object and use innerHTML (or any other property that suits your needs) to assign it.
element.innerHTML = strHTMLText;
The rest is done by Internet Explorer, which displays the page after all the involved behaviors have finished responding to the ondocumentready event.
      The ondocumentready event fires when the browser has processed the contents of the whole page to which the behavior belongs. Using the oncontentready event is fine as well, and it allows you to intervene a little earlier in the page-building process. Oncontentready, in fact, is fired when the content of the element to which the behavior is attached has been completely parsed. With both oncontentready and ondocumentready, you avoid the pitfalls that you encounter when you rely on onload.

Attaching Behaviors to HTML Tags

      A behavior is just a standalone software component and, as of Internet Explorer 5.0, there was nothing to establish an implicit and automatic association between it and a specific HTML tag. At this point you're probably wondering, "How do I link a behavior to an HTML tag?" Normally you use a new Internet Explorer-specific CSS style called (you guessed it) behavior.
dino\:tabstrip {
behavior:url(tabstrip.htc);
}
With this kind of declaration, the display content of a tag like <dino:tabstrip> is entirely determined by the tabstrip.htc behavior, allowing for HTML code like this:
<dino:tabstrip 
 height="150" label="Expoware Soft">
 <tabitem title="Writing"       src="one.htm" />    
 <tabitem title="Training"      src="two.htm" />    
 <tabitem title="Consulting"    src="three.htm" />    
 <tabitem title="Contact us"    src="four.htm" selected />    
</dino:tabstrip>
      Figure 1 shows the dialog produced by the previous code. You can obtain the source code for this behavior from the link at the top of this article. The page shows a tab stripâ€"a logical construct that can't be easily expressed with any existing HTML tag. The previous syntax, however, makes it particularly clear and easily manageable. I discussed this code and how to create a personal library of HTML tags in my May 2000 Cutting Edge column. (I find it particularly exciting that due to these features, an XML namespace can be considered a dynamic library of HTML functions.)

Figure 1 Tab Strip
Figure 1 Tab Strip

      Another possibility to link tags and behaviors is provided by the addBehavior DOM method. You can call this method from the context of any HTML element. The effect is immediate, and this adds a great deal of flexibility to the solution since it allows for extremely dynamic behaviors.
      Bear in mind that a behavior attached through the addBehavior method is not automatically detached if the element disappears from the DOM (by being deleted). The same applies to behaviors that are attached through inline style declarations, using the style attribute in the HTML tag. If you attach a behavior through a CSS rule in the <style> header, as shown previously, then the behavior is automatically detached as soon as the element is moved out of the DOM.

The Drawbacks of Behaviors

      At this point, behaviors might look like the perfect tool for creating custom HTML tags in Internet Explorer 5.0. But there's a subtle problem. Behaviors as you know them are not tied to specific tags; they can be used to provide new functionality to a number of different tags. Elements, in turn, can display more behaviors at the same time. In other words, attached behaviors are function-oriented components that aim to provide a certain behavior without regard for the underlying element, independently from other current settings.
      You might think that using behaviors to power custom HTML tags is only one possible useâ€"not their primary use, but perhaps the most exciting one. As I'll show you in a moment, though, an HTML tag has a number of requirements that must be met. Unfortunately, attached behaviors don't meet them all.
      First, an HTML tag must be inextricably associated with exactly one software module that provides its core behavior. It can then be specialized by CSS styles, including attached behaviors. Second, the association between the tag and the software module must be successfully resolved before the page loads completely. Furthermore, it must be impossible to separate this module from the tag. Put another way, tags and behaviors must spend their whole lifetime together. Have you ever seen one <INPUT> tag that creates both a textbox and a color picker? The code in Figure 2 produces the color picker dialog in Figure 3. Here you can see the subtlety in behavior programming.

Figure 3 Color Picker Page
Figure 3 Color Picker Page

<INPUT ID="theColor" TYPE="text">
<INPUT ID="picker" TYPE="text" CLASS="colorpick" 
    VALUE="#FFFF00" ONCHANGE="fnColorChange()">
      The colorpick class transforms a plain old textbox into a cool color picker component. What's the magic of the colorpick class? It's simply an attached behavior:
<style>
    .colorpick    {behavior: url(ColorPick.htc)}
</style>
Among other things, what's great about this approach is that you can use the same element to show different behaviors at different times. The source code in Figure 4 comes from a Platform SDK sample; Figure 5 shows the page in action. By clicking on different links you can attach or detach a certain behavior using the addBehavior and removeBehavior methods.

Figure 5 Behavior Choices
Figure 5 Behavior Choices

      In this case, the behavior is offering a simple, tag-independent functionality, like a highlighting effect as you hover over each item in the list. You can enable and disable this functionality as you please.
      If you want to create and use completely custom HTML tags, attached behaviors aren't the ideal approach. They have several flaws:
  • They can be detached at any time.
  • They can be applied to any tag and they aren't aware of the specific tag name which they've been designed for and associated with.
  • They work asynchronously with respect to page loading.
  • You never know whether a behavior attached successfully.
  • You never know the exact moment when a behavior attached to an element.
  • The addBehavior method works asynchronously, and you can't be sure the behavior has been successfully added when the next instruction is processed. In addition, there's no page-level event to let you know when a given behavior is attaching. In contrast, you have an ondetach event that indicates when a behavior is going to be detached.
      To create custom HTML tags, you need a pattern that closely imitates the handling of a standard HTML tag. You can never break the link between, say, a <TABLE> tag and the code that manages it. Furthermore, this association is set before the page is published to the browser. In other words, each standard HTML tag is associated with exactly one precise behavior in a synchronous manner. Such an association is irrevocable and lasts until the page is unloaded. You cannot obtain these features with behaviors in Internet Explorer 5.0.

Element versus Attached Behaviors

      Internet Explorer 5.5 introduces another type of DHTML behavior and renames the former class to attached behaviors. This new type of behavior is called element behaviors to emphasize the connection between the behavior (a software module) and a specific element (a custom HTML tag). An element behavior is irrevocably associated with a specific tag upon page loading. The name of this tag is hardcoded in the behavior's source. An element behavior cannot be handled programmatically, or detached or disabled programmatically. An element behavior is functionally equivalent to the browser's internal code that handles default HTML tags such as <IMG>. You can't replace the handler for <IMG> and you can't replace the handler for any custom tag with an element behavior either.
      Codewise, an element behavior differs from an attached behavior in only a few ways, but the browser treats them quite differently. To transform an attached behavior into an element behavior, just give it the following schema:
<PUBLIC:COMPONENT tagName="TABSTRIP">
<SCRIPT>
•••
</SCRIPT>
</PUBLIC:COMPONENT>
Simply specify the tagName attribute to specify the name of the custom HTML element to which the behavior should be connected. No other changes are required.
      An element behavior can also work as an attached behavior, depending on how you refer to it from the calling page. If you link it through CSS or DOM methods, it'll be an attached behavior. If you want it to work as an element behavior, you must follow a different policy for initialization.
      The real difference between element behaviors and attached behaviors lies not so much in the way you write them, but in the way in which Internet Explorer 5.5 processes them. Element behaviors feature a one-to-one association with a precise custom HTML tag and need to be declared on the top of the page as part of a well-known namespace. There's a new directive to accomplish this:
<?IMPORT 
namespace="dino" 
implementation="textframe.htc" >
      If, like me, you are still relying very heavily on Win32®, you'll soon realize that the <?IMPORT> directive works in much the same way as one of the handiest Visual C++® features, shown here:
#pragma comment(lib, "thedll.lib")
This #pragma in the previous code programmatically forces the linker to get the specified DLL import library in the final executable. Likewise, the <?IMPORT> directive forces the browser to import the element behavior to which the implementation attribute points. The HTC must be written according to the rules of element behaviors and must hardcode the name of the custom tag it refers to. While importing a new element into the context of the current HTML page, you associate it with a namespace. The namespace must be specified twice: once in the <HTML> root tag and once in the <?IMPORT> directive.
<html xmlns:dino>
<?IMPORT 
namespace="dino" 
implementation="textframe.htc" >
      Assuming that textframe.htc looks like the code in Figure 6, from now on you can use the <dino:textframe> to surround a text with a border. Figure 7 shows the result of the following code:
<HTML xmlns:dino>
<?IMPORT 
namespace="dino" 
implementation="TextFrame.htc" >
<BODY>

<dino:textframe>
Expoware <b>Soft</b>
</dino:textframe>

<hr noshade style="height:10px">
the rest of the page...
</BODY>
</HTML>
      Of course, the name of the namespace can vary; the sole constraint is that it must be unique within the page. If the HTC file is not an element behavior, you might receive an error message or the tag might display incorrectly or even be invisible.

Figure 7 Text Border
Figure 7 Text Border

      If you use the textframe.htc element behavior with Internet Explorer 5.0, and link it to an HTML tag in the old-style CSS-based way, it will work just fine as an attached behavior. How is this possible, since attached and element behaviors have different root tags? When parsing the behavior's source, Internet Explorer only checks to see that the XML is well-formed, but doesn't validate the tags against a document type definition (DTD) or a schema.

Behaviors Tips

      One question you may have at this point is: "How can I import more behaviors?" You can do that by repeating the <?IMPORT> directive as many times as you need. You can use either the same namespace or define new ones. The following snippet imports two behaviors through the same namespace.
<HTML xmlns:dino>
<?IMPORT 
namespace="dino" 
implementation="Behav1.htc" >
<?IMPORT 
namespace="dino" 
implementation="Behav2.htc" >
If you need to keep them separate (with both behaviors mapping to the same tag name), then you should use different namespaces.
<HTML xmlns:dino1 xmlns:dino2>
<?IMPORT 
namespace="dino1" 
implementation="TextFrame1.htc" >
<?IMPORT 
namespace="dino2" 
implementation="TextFrame2.htc" >
<BODY>
<dino1:textframe>Hello world!</dino1:textframe>
<dino2:textframe>Hi</dino2:textframe> 
      When using a custom tag, always remember to close the tag, either using the closing tag or through the short XML syntax.
<dino2:textframe>Hi</dino2:textframe> 
<dino2:textframe />
If you forget to properly close the tag, you won't get a syntax error, but you'll get unpredictable results. So whenever your output is not what you expected, check that all custom tags are closed.
      Looking at recent Platform SDK and MSDN documentation, the <PUBLIC:ATTACH> tag is given the following syntax:
<PUBLIC:ATTACH
    EVENT = sEvent
    FOR = document | element | window
    ONEVENT = sEventHandler
    ID = sID
/>
The FOR clause defaults to element, so you rarely need it. Instead, pay attention to ONEVENT clause. It specifies the name of the procedure that will handle the event. Some old Internet Explorer 5.0 documentation recommended the use of the HANDLER clause for the same purpose. With Internet Explorer 5.5 you can use both, although this is not explicitly mentioned. Playing around with behaviors, I noticed only a subtle difference in the way you specify the procedure name. With ONEVENT you can terminate the procedure name with or without the final parentheses and VBScript code is searched first. With HANDLER (which is an obsolete clause), you cannot have trailing parentheses in the handler's name and JScript code is always required.
<PUBLIC:ATTACH 
EVENT="ondocumentready" 
onevent="DoInit()" />
If you have a DoInit VBScript function, the behavior won't work. If you have both JScript® and VBScript code blocks, again, VBScript blocks are searched first. Why use the onevent clause? You would use it if you have Internet Explorer 5.0 behaviors and you don't want to modify that code.
      With Windows® 98 and Windows 2000 in particular, you can use the automatic document preview when a file name is selected within Explorer (see Figure 8). Notice that when the HTML file is selected, any behavior present in the page will receive both the oncontentready and ondocumentready events.

Figure 8 Document Preview
Figure 8 Document Preview

Literal Content

      One of the reasons you might want to write element behaviors is to create your own data island tags. A data island tag inserts raw text into the page. This text, regardless of its content, won't be parsed by the browser. A well-known data island is the Internet Explorer 5.0 <xml> tag that allows you to store invisible XML code in any HTML page.
      If you want your element behavior to act as a data island, just set the literalContent attribute of <PUBLIC:COMPONENT> to true. Figure 9 shows a free adaptation of the behavior discussed by Dave Massy in the January/February 2000 issue of MSDN News. That behavior embeds the specified file in the page without affecting the rendering of the host page. Run the following code and click the button to verify that it works.
<HTML xmlns:dino>
<?IMPORT 
namespace="dino" 
implementation="Include.htc" >
<SCRIPT language=JScript>
function Click() {
    alert(document.body.outerHTML);
}
</SCRIPT>
<BODY>
<dino:include src="two.htm"/>
<input type=button onclick="Click()">
</BODY>
</HTML>
      Behaviors with literal content also receive the oncontentsave event when someone attempts to read or save their content.

The ViewLink Technology

      When you import an element behavior component in an HTML page, you end up importing all DHTML trees that it creates. For example, in Figure 1 you saw a tabstrip component embedded in an HTML page through a simple and intuitive group of custom tags, such as <tabstrip> and <tabitem>. By importing them, all of the subtrees that form the body of the new element are slipstreamed in the page. Depending on what you really need to do, this may or may not be a problem. In general, it violates the component's encapsulation. In fact, you don't insert a standalone component in the page, but a piece of code that fully integrates with the rest of the page. It inherits and affects CSS stylesheets and the flow of events. Be aware of this when you write your behavior.
      ActiveX® controls and DHTML scriptlets hide their internal structure and can be considered standalone components that are linked to the page. They do not extend the page's object model with their content. Neither ActiveX controls nor DHTML scriptlets can be associated with a custom tag.
      Internet Explorer 5.5 supports a new technology, called ViewLink, that enables you to link an element behavior to the hosting page, as opposed to embedding the whole subtree. Figure 10 shows a nice tool called HTML Spy in action. I borrowed it from the CD that accompanies Programming Microsoft Internet Explorer 5.0 by Scott Roberts (Microsoft Press, 1999). HTML Spy displays a page through WebBrowser, hooks on the onmouseover event, and detects the tag name and the element ID of the element underneath the mouse pointer.

Figure 10 Detecting Tags in HTML Spy
Figure 10 Detecting Tags in HTML Spy

      The page you see in Figure 10 contains the tabstrip behavior. This component contains one table for each tab. You can test it by moving the mouse over one of the tabs. Add a button to the HTML page to dump out the content of the tabstrip component
<SCRIPT>
function Click() {
    alert(theTabStrip.outerHTML);
}
</SCRIPT>
<BODY>
<input type=button onclick="Click()">
and you'll see all the information about the various tables that form the tabstrip and all the implementation details of the component.
      Figure 11 shows the result once ViewLink has been enabled for the component. All the parts of the tabstrip component now combine to look like a whole.

Figure 11 HTML Spy with ViewLink
Figure 11 HTML Spy with ViewLink

Enabling ViewLink

      You can enable the ViewLink feature in your element behaviors in two ways: declaratively or programmatically. To enable it declaratively, just add the following line within the body of the <PUBLIC:COMPONENT> tag:
<PUBLIC:DEFAULTS viewLinkContent=True />
After this line, all the output of the behavior will combine to form an independent subtree that's linkedâ€"not embeddedâ€"to the primary HTML document. To enable ViewLink programmatically, add the following line instead:
defaults.viewLink = document;
      Defaults is an Internet Explorer 5.5 scriptable object that programmatically sets default properties on an element behavior. Its viewLink property contains the document object that the master element connects to. The master element is the custom HTML tag to which you attached this view-linked element behavior.
      The ViewLink enables the document fragment to be rendered as content in the primary document (the HTML page that hosts the behavior). However, it could also be a parent behavior that links to the content of a child behavior. As you may have guessed <PUBLIC:DEFAULTS> is the declarative form of the defaults object. This object has a number of interesting properties to set behavior-specific CSS styles or to integrate it with the tab sequence of the master document. (See the MSDN documentation for more details.)
      Once you've enabled the viewLink mechanism, you must change a few lines in your behavior's code. In particular, you should use document.body instead of element to output any HTML text representing the element subtree. Assuming that htmlText contains the HTML string for the element subtree, instead of
element.innerHTML = htmlText;
use
document.body.innerHTML = htmlText;
This ensures that the HTML text slips into a new DOM that's hidden from view. Element, in fact, refers to an injection point located in the primary documentâ€"exactly what you want to avoid.

ViewLink Considerations

      All the styles you apply to the custom tag affect the view-linked behavior. However, styles can be overriden within the context of the view-linked subtree. A style property set inside the HTC file takes precedence over any other style attribute set outside it.
      Interesting considerations apply to event routing, too. Consider the tabstrip.htc component. It is made of a number of tables and intercepts the onmouseover and onclick events to change the selection from tab to tab. When you write it as non-viewLink element behavior, all the HTML source code that renders it gets embedded in the hosting document. Run the file e_default.htm from the source code and click on the button on the top of the page. It shows the actual body of the document. The structure looks like this:
<dino:tabstrip>
<table>
<!-- all the HTML code  -->
</table>
</dino:tabstrip>
This is the result of
element.innerHTML = htmlText;
where element represents <dino:tabstrip>. The table is therefore part of the main document. It turns out that to detect mouse events you need to hook for the events at the page level, thereby justifying the various <PUBLIC:ATTACH> tags for onclick and onmouseover.

Figure 12 Linking with ViewLink
Figure 12 Linking with ViewLink

      When you move to ViewLink, you're linking to a completely separate page (see Figure 12). Consequently, the mouse events must be handled internally by this new page. All the <PUBLIC:ATTACH> tags refer to events at the page level that the view-linked behavior cannot detect. The reason is quite obvious: the event occurs locally and the behavior itself would be responsible for bubbling it up to the primary page. The event would become detectable through <PUBLIC:ATTACH> only after the behavior has bubbled it up! As you can see, there's no reason whatsoever to justify this maze of messages. When writing a ViewLink behavior, just think of it as a new pageâ€"like a DHTML scriptlet. If it's local events you're looking to hook, catch them through an event handler, located at the behavior's <BODY> level.
<BODY onmouseover="DoMouseOver()"
      onmouseout="DoMouseOut()"
      onclick="DoClick()">
</BODY>
      The previous code replaces the following code in a ViewLink behavior:
<PUBLIC:ATTACH EVENT="onclick" HANDLER="DoClick" />
<PUBLIC:ATTACH EVENT="onmouseover" HANDLER="DoMouseOver" />
<PUBLIC:ATTACH EVENT="onmouseout" HANDLER="DoMouseOut" />
Incidentally, you could also leave this code in, but it is dead code, hopelessly waiting for events that occur at an inner level and that will never get bubbled up.

A Call to Action

      In this month's source archive you'll find three flavors of the tabstrip behavior: attached, element, and ViewLink. Starting from the old-style version, it took me a few lines of code to adapt it to work as an element behavior and then as a view-linked component.
      Behaviors are the way to go when you're building client-side Web components; and they utilize custom HTML. They are a great way to build Web solutions for Internet Explorer 5.x.
      In your development efforts, beware that behaviors are plain text, subject to spying and tampering and, more importantly, subject to security rules like any other HTML page. Binary behaviors will be a perfect workaround for this kind of problem. Stay tuned!
Dino Esposito is a trainer and consultant based in Rome. Author of several books by Wrox Press, he now spends most of his time teaching classes on ASP(+) and ADO(+). Get in touch with Dino at desposito@vb2themax.com.

From the December 2000 issue of MSDN Magazine

Page view tracker