Using HTML Components to Implement DHTML Behaviors in Script

This topic documents a feature of Binary Behaviors, which are obsolete as of Internet Explorer 10.

Dynamic HTML (DHTML) behaviors are components that can be implemented in a number of ways. For several years, the task of writing components has been limited to developers using Microsoft Visual C++, Microsoft Visual Basic, and Java.

As of Microsoft Internet Explorer 5, script developers can use HTML Component (HTC) or Windows Script Component (WSC) to implement their own DHTML behaviors in script, using Visual Basic Scripting Edition (VBScript), Microsoft JScript (compatible with ECMA 262 language specification), or any third-party scripting language that supports the Microsoft ActiveX Scripting interfaces.

This article outlines the steps for using HTC files to create behaviors in script. For more information about how to create behaviors using WSC, see Microsoft Windows Script Technologies.

This article assumes that the reader has a good understanding of script programming and is familiar with HTCs and DHTML behaviors. Implementing behaviors in Visual C++ is beyond the scope of this article. For more information about DHTML behaviors, HTCs, WSCs, and the Visual C++ interfaces involved in implementing behaviors, see Related Topics.

After reading this article, you should be able to implement behaviors in your scripting language of choice, isolate script from your content, and reap the benefits of encapsulation and code reusability in your Web site.

This article is divided into the following sections:

  • HTC Overview 
  • Creating an HTC 
  • Exposing Properties 
  • Using the INTERNALNAME Attribute 
  • Exposing Methods 
  • Exposing Custom Events 
  • Receiving Notifications 
  • Scope Rules 
  • Timing Considerations 
  • Behavior-Related Enhancements to the DHTML Object Model 
  • Related Topics 

HTC Overview

Introduced in Internet Explorer 5, HTCs provide a simple mechanism to implement DHTML behaviors in script. An HTC file is nothing but an HTML file, saved with an .htc extension, that contains scripts and a set of HTC-specific custom elements that expose properties, methods, and events that define the component.

As an HTML file, an HTC provides the same access as DHTML to all elements on the page. This means that within an HTC, all HTC elements are accessible from script as objects using their id attributes. This allows all attributes and methods of HTC elements to be dynamically manipulated through script as properties and methods of these objects.

You can use HTCs to implement behaviors that:

  • Expose properties and methods. These properties and methods are defined using the PUBLIC:PROPERTY and PUBLIC:METHOD elements, respectively. For more information about exposing properties and methods, see Exposing Properties and Exposing Methods.
  • Expose custom events. These events are defined using the PUBLIC:EVENT element and are fired back to the containing page using the element's fire method. Additionally, the createEventObject method is provided to allow the HTC to set event context information when firing the event. For more information about exposing events, see Exposing Custom Events.
  • Access the containing page's DHTML Object Model. The element object in HTCs returns the element to which the behavior is attached. With this object, an HTC can access the containing document and its object model (properties, methods, and events).
  • Receive notifications. Using the ATTACH element, the browser notifies the HTC of standard DHTML events as well as two HTC-specific events, oncontentready and ondocumentready.

For more information about each of the HTC elements, properties, methods, and events described in this section, see HTC Reference.

Creating an HTC

The steps for creating an HTC are demonstrated in the following example, which implements a mouseover highlight effect.

  1. Create a skeleton HTC file. A typical HTC file consists of a script block and an optional enclosing COMPONENT block. It is saved with an .htc extension.

    <PUBLIC:COMPONENT>
        <SCRIPT>
        </SCRIPT>
    </PUBLIC:COMPONENT>
    
  2. Fill in the script block with the code to implement the behavior.

    In the following code, the ATTACH element is used to set up the HTC to receive notifications as to when the onmouseover and onmouseout events are fired on the element. This allows the HTC to toggle the color of the element to achieve the desired highlighting effect as the mouse hovers over the element.

    <PUBLIC:COMPONENT>
    <PUBLIC:ATTACH EVENT="onmouseover" ONEVENT="Hilite()" />
    <PUBLIC:ATTACH EVENT="onmouseout"  ONEVENT="Restore()" />
    <SCRIPT LANGUAGE="JScript">
       var normalColor, normalSpacing;
    
       function Hilite()
       {
         // save original values
         normalColor  = runtimeStyle.color;
         normalSpacing= runtimeStyle.letterSpacing;
    
         runtimeStyle.color  = "red";
         runtimeStyle.letterSpacing = 2;
       }
    
       function Restore()
       {
         // restore original values
         runtimeStyle.color  = normalColor;
         runtimeStyle.letterSpacing = normalSpacing;
       }
    </SCRIPT>
    </PUBLIC:COMPONENT>
    

    Note  As mentioned in the HTC overview, the element object returns the element to which the behavior is attached, therefore providing a means to access the element's object model. Alternatively, you can access the properties, methods, and events of the element by using the property, method, or event name directly, without prefixing it with the element keyword. In the preceding example, notice how the color was toggled by referring directly to the runtimeStyle property, instead of using element.runtimeStyle, which is also acceptable.

  3. Once implemented, this HTC can be used on a page to achieve the desired mouseover highlighting effect.

    <HEAD>
    <STYLE>
       LI {behavior:url(hilite.htc)}
    </STYLE>
    </HEAD>
    
    <P>Mouse over the two list items below to see this effect.
    <UL>
      <LI>HTML Authoring</LI>
      <LI>Dynamic HTML</LI>
    </UL>
    

    Code example: https://samples.msdn.microsoft.com/workshop/samples/author/behaviors/hilite2.htm

Exposing Properties

An HTC can expose its own set of properties using the PROPERTY element.

For example, the preceding mouseover highlight example can be extended to expose a hiliteColor property that allows you to set the color of the text to be highlighted. You can do this by using the PUBLIC:PROPERTY element to define the property, as shown in the following code.

<PUBLIC:PROPERTY NAME="hiliteColor" />

Once the property is defined, it can be referred to in the HTCs code, as shown in the following example. Notice how this code sets the value of the property to red if the containing page does not specifically set its value.

<PUBLIC:PROPERTY NAME="hiliteColor" />
<PUBLIC:ATTACH EVENT="onload" FOR="window" ONEVENT="initColors()"  />
<SCRIPT LANGUAGE="JScript">
function initColors()
{
  // initialize the property
  hiliteColor = (hiliteColor == null) ? "red" : hiliteColor;
}
</SCRIPT>

The following example applies the mouseover highlight effect to a table of contents, changing the color of the anchors as the mouse hovers over the text. Notice how the first list changes the color of the anchors to green when hovered over, whereas the second list simply changes the anchor colors to the default color, red. As explained in the preceding paragraph, if the containing page does not specifically set the hiliteColor property, the color defaults to red.

<style>
   .CollapsingAndHiliting {behavior:url(ul.htc) url(hilite2.htc)}
   A           {behavior:url(hilite2.htc)}
</style>

<UL>
  <LI CLASS="CollapsingAndHiliting" CHILD="Topics1">HTML Authoring</LI>
  <UL ID="Topics1">
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Beginner's Guide</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">IE4.0 Authoring Tips</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">HTML Coding Tips</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Table Cell Backgrounds</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Drop Caps</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Quote Server</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">HTML Wizard</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Dr. HTML</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">HTML Coding FAQ for Internet Explorer</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">SGML DTD for Internet Explorer 3.0 Markup</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Authoring Basics</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Authoring Effective Pages</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Designing Efficient Pages</A></LI>
      <LI><A HILITECOLOR="green" HREF="/workshop/author/default.asp">Using Frames</A></LI>
  </UL>

   <LI><A HREF="/workshop/author/default.asp">HTML Help Authoring</A></LI>
   <LI CLASS="CollapsingAndHiliting" CHILD="Topics2">HTML References</LI>

   <UL ID="Topics2">
      <LI><A HREF="/workshop/author/default.htm">Elements</A></LI>
      <LI><A HREF="/workshop/author/default.htm">Character Sets</A></LI>
   </UL>

   <LI CLASS="CollapsingAndHiliting" CHILD="Topics3">HTML Applications (HTA)</LI>
   <UL ID="Topics3">
      <LI><A HREF="/workshop/author/default.htm">Overview</A></LI>
      <LI><A HREF="/workshop/author/default.htm">Reference</A></LI>
   </UL>
</UL>

Code example: https://samples.msdn.microsoft.com/workshop/samples/components/htc/toc/toc2.htm

Note  A behavior can override an element's default behavior by exposing a property of the same name as that which is already defined for the element. For example, a behavior that exposes an href property can effectively override the a element's default href property, if that behavior is applied to an a element on a page.

Using the INTERNALNAME Attribute

When exposing a property in an HTC, it is possible to specify both a NAME attribute and an INTERNALNAME attribute in the PUBLIC:PROPERTY element. If both names are specified, the NAME is used to refer to the property from the containing document, while the INTERNALNAME is used to refer to the same property within the HTC.

When specifying an INTERNALNAME attribute on the property, the variable name needs to be declared globally and initialized before it can be referenced anywhere in the component. Failure to do so results in either a scripting error indicating that the variable name is undefined, or an incorrect property value retrieved by the containing document.

Specifying either a PUT or GET attribute in the PUBLIC:PROPERTY declaration causes the INTERNALNAME attribute to be ignored, if the INTERNALNAME is specified. Setting and/or retrieving the value of the property through the function(s) specified in the PUT and GET attributes takes precedence over setting and/or retrieving the value of the property through the INTERNALNAME.

Exposing Methods

Using the PUBLIC:METHOD element, it is possible for an HTC to expose its own set of methods, as shown in the following code.

<PUBLIC:METHOD NAME="startFlying" />

Once the method is defined, the method can be used directly in the HTC's code, as shown in the following example, which implements a flying effect on groups of text on a page.

<PUBLIC:METHOD NAME="tick" />
<PUBLIC:METHOD NAME="startFlying" />
:
<SCRIPT LANGUAGE="JScript">
var currCount;
var flyCount;
var flying;
var msecs;

var oTop, oLeft;
msecs = 50;
flyCount = 20;
flying = false;

runtimeStyle.position = "relative";
runtimeStyle.visibility = "hidden";

window.attachEvent("onload", onload);

function onload()
{
// delay commences from the window.onLoad event
  if (delay != "none")
  {
     window.setTimeout(uniqueID+".tick()", delay);
  }
}

function tick()
{
   if (flying == false)
   {
      startFlying();
   }
   else
   {
      doFly();
   }
}

function startFlying()
{
  if (fromX==null && fromY==null)
  {
     if (from=="top")
     {
       runtimeStyle.posTop = -offsetTop-offsetHeight;
     }
     else if (from=="bottom")
     {
       runtimeStyle.posTop = element.document.body.clientHeight;
     }
     else if (from=="right" )
     {
       runtimeStyle.posLeft = element.document.body.clientWidth;
     }
     else
     {
       runtimeStyle.posLeft = -offsetLeft-offsetWidth;
     }
  }
  else
  {
     runtimeStyle.posTop = fromY;
     runtimeStyle.posLeft = fromX;
  }

  runtimeStyle.visibility = "visible";
  flying = true;

  oTop = runtimeStyle.posTop;
  oLeft = runtimeStyle.posLeft;

  currCount = 0;
  doFly();
}

function doFly()
{
  var dt, dl;

  currCount++;
  dt = oTop / flyCount;
  dl = oLeft / flyCount;

  runtimeStyle.posTop -= dt;
  runtimeStyle.posLeft -= dl;

  if (currCount < flyCount)
  {
      window.setTimeout(uniqueID+".tick();", msecs);
  }
  else
  {
      runtimeStyle.posTop = 0;
      runtimeStyle.posLeft = 0;
      flying = false;
      evObj = createEventObject();
      evObj.setAttribute("resulty", uniqueID);  
      finished.fire(evObj);
  }
}
</SCRIPT>

Code example: https://samples.msdn.microsoft.com/workshop/samples/author/dhtml/CustomTags/CustomFlying.htm

Note  Similar to the PUBLIC:PROPERTY element, it is possibly to specify an INTERNALNAME in the METHOD declaration, so that the name by which the method is referred to in the containing page is different from the name it is referred to within the HTC. Additionally, a behavior can override an element's default behavior by exposing a method of the same name as that which is already defined for the element. For example, a behavior that exposes a click method can effectively override the a element's default click method, if that behavior is applied to an a element on a page.

Exposing Custom Events

Using the PUBLIC:EVENT element, an HTC can define custom events and expose them to the containing page.

The following code demonstrates how a calculator, implemented as an HTC, defines a custom event—onResultChange—using the EVENT element. When the event is fired to the containing page, the resulting value is passed back to the page, setting an expando property—result—of the event object.

<PUBLIC:EVENT NAME=onResultChange ID=rcID />
<SCRIPT LANGUAGE="JScript">
:
oEvent = createEventObject();
oEvent.result = sResult;
rcID.fire (oEvent);
:
</SCRIPT>

The following example shows what the containing page should look like.

<HTML XMLNS:InetSDK>
<HEAD>
<TITLE>Calculator Sample</TITLE>

<STYLE>
   INPUT    {font-family: Courier New}
   @media all {
      InetSDK\:CALC {behavior:url(Engine.htc)}
   }
</STYLE>

<LINK REL="stylesheet" HREF="/workshop/basicSDKIE4.css" TYPE="text/css">
</HEAD>

<BODY BGCOLOR="#FFFFFF">
<BLOCKQUOTE CLASS="body">

<P>
<InetSDK:CALC id="myCalc" onResultChange="resultWindow.innerText =window.event.result">
<TABLE>
<TR>
<TD COLSPAN=5>
<DIV ID="resultWindow" STYLE="padding:5; background-color:buttonface" ALIGN="RIGHT">0.</DIV>
</TD>
</TR>
<TR>
    <TD><INPUT TYPE=BUTTON VALUE=" 7 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 8 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 9 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" / "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" C "></TD>
</TR>
<TR>
    <TD><INPUT TYPE=BUTTON VALUE=" 4 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 5 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 6 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" * "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" % " DISABLED></TD>
</TR>
<TR>
    <TD><INPUT TYPE=BUTTON VALUE=" 1 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 2 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" 3 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" - "></TD>
    <TD><INPUT TYPE=BUTTON VALUE="1/x" DISABLED></TD>
</TR>

<TR>
    <TD><INPUT TYPE=BUTTON VALUE=" 0 "></TD>
    <TD><INPUT TYPE=BUTTON VALUE="+/-"></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" . "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" + "></TD>
    <TD><INPUT TYPE=BUTTON VALUE=" = "></TD>
</TR>
</TABLE>

</InetSDK:CALC>
</BLOCKQUOTE>

</BODY>
</HTML>

Code example: https://samples.msdn.microsoft.com/workshop/samples/components/htc/calc/calc.htm

Note  A behavior can override an element's default behavior by exposing an event of the same name as one that is already defined for the element. For example, a behavior that exposes an onclick event can effectively override the a element's default onclick event, if that behavior is applied to an a element on a page.

Receiving Notifications

An HTC can attach to events being fired on the containing page through the ATTACH element. Using this element, the HTC can attach to standard DHTML events, as well as to HTC-specific events listed in the HTC Reference.

Note   The HTC can also use the attachEvent method to receive notifications from the containing page. However, there are disadvantages:

  • The attachEvent method allows you to attach only to standard DHTML events, and none of the HTC-specific events.
  • The HTC continues to receive notifications from the containing page even after the behavior is detached from the element. However, the HTC could call the detachEvent method when the ondetach event fires. Behaviors that attach to events on the page using the ATTACH element automatically stop receiving notifications when the behavior is detached from the element.

Scope Rules

When working with HTCs, three namespaces are involved: the behavior's, the element's, and the containing page's. A set of scope rules have been defined for HTCs to help establish the order in which name conflicts are resolved. The order of precedence used in conflict resolution, from highest to lowest, is as follows:

  • The behavior
  • The element to which the behavior is attached
  • The window object of the containing document

This means that if an HTC defines a global variable called event, any reference to event in the HTC will resolve to that global variable. The correct way to refer to the window object's event property within that HTC is to use window.event.

In the following example, note how the names have been resolved using the preceding scope rules.

  • attachEvent - resolves to the attachEvent method of the element. This is the same as calling element.attachEvent.
  • normalColor - resolves to the global variable defined by the behavior on top of the script.
  • runtimeStyle - resolves to the runtimeStyle property of the element.
<SCRIPT LANGUAGE="JScript">
var normalColor, normalSpacing;

attachEvent("onmouseover", event_onmouseover);
attachEvent("onmouseout",  event_onmouseout);

function event_onmouseover()
{
  // save original values
  normalColor  = style.color;
  normalSpacing= style.letterSpacing;

  runtimeStyle.color  = "red";
  runtimeStyle.letterSpacing = 2;
}

function event_onmouseout()
{
  // restore original values
   runtimeStyle.color  = normalColor;
   runtimeStyle.letterSpacing = normalSpacing;
}
</SCRIPT>

Timing Considerations

When authoring behaviors, it is important to know when the behavior is applied to the element. Until the behavior is applied, access to the values of behavior-defined properties, which might be set from the document, might not be available to scripts.

To ensure that the behavior is completely downloaded and applied to the element, it is important to wait for the onreadystatechange event to fire, and then verify that the readyState property of the element is set to complete. Until this event fires, attempting to use any of the behavior-defined members before the behavior is attached to the element can result in a scripting error indicating that the object does not support that particular property or method.

Note   As the behavior and its containing document are parsed and loaded, the behavior receives notifications of progress through the oncontentready and ondocumentready notifications. The oncontentready notification is received when the content of the element has been completely parsed. At this point, a behavior can count on the innerHTML property of the element to return the correct value. The ondocumentready notification is received when the document has been completely downloaded, including all scripts, images, ActiveX controls, and all other files on the page that need to be downloaded separately.

As of Internet Explorer 5, the following enhancements were made to the DHTML Object Model to add support for behaviors.

  • The proposed Cascading Style Sheets (CSS) behavior attribute applies the behavior to the element.
  • The attachEvent and detachEvent methods enable an HTC to receive, and to stop receiving, event notifications from the containing page by specifying a function that is called whenever the event fires on the object.
  • The uniqueID property enables an HTC to assign an id attribute to the element. This can be useful in cases where an HTC injects code into the containing page and needs to specify the id of the element to which the behavior is being applied.
  • The addBehavior and removeBehavior methods provide a dynamic way to attach and detach a behavior to an element on a page without using CSS.
  • The behaviorUrns collection identifies the URNs of all behaviors currently attached to the element.
  • The srcUrn property of the event object specifies the URN of the behavior that fired the event.
  • The scopeName and the tagUrn properties collectively identify the namespace defined for the element.
  • The urns method identifies all the objects to which a specified behavior is currently attached.

The following links provide additional information about DHTML behaviors.