SiteExperts.com Logo Home | Community | Developer's Paradise | Jobs
User Groups | Site Tools | Site Information | Search

Inside Technique : HTML Text Editor : Creating Behaviors

DHTML Behaviors are created using XML. While HTML is defined by a fixed set of tags and semantics, XML allows custom markup to be defined for packaging meta-information and data. Below is the XML for defining a minimal DHTML behavior. Notice the special tags for specifying the scriptlet's definition:

<scriptlet>
  <implements id="behavior" type="behavior" default/>
  <script language=JScript>
    // define behavior here
  </script>
</scriptlet>

The scriptlet tag specifies we are creating a scriptlet, and the implements tag defines this scriptlet to be a DHTML Behavior. The implements element represents the COM interface handler for the scriptlet, where behaviors are an interface recognized by Internet Explorer 5.0. Therefore, with scriptlets, you can actually create reusable COM components that are unrelated to the browser. Creating non-behavior COM components is beyond the article (see Microsoft Sitebuilder's for an article on scriptlet components). In addition, the implements element is also used to specify any custom methods, properties, and events exposed by the component. We will provide a more in-depth tutorial on creating behaviors and their interfaces in a future article.

The functionality for the scriptlet is contained within the script block. The script can be written in JavaScript/ JScript, VBScript or any language you may have installed. Within the script block, you write the appropriate functions and event handlers for processing the behavior.

Usually the first step is to define what events you want to receive from the browser. This is accomplished with the attachEvent() method. With this method you specify the event you want to catch, and a function to execute when it is loaded. The attachEvent() method is used over the traditional event property model where you assign a function directly to the property to allow broadcasting of the event. This is necessary so behaviors augment existing functionality and do not replace it. For example, the behavior's onclick event should not prevent the element itself, or possibly other behaviors, from receiving the same event.

To attach the click event, we used the following line of script:

  attachEvent("onclick", checkFormat)

  function checkFormat() {
    // clicked
  }

In the case of our behavior, we want to replace the existing HTML in the document with new HTML. This is done by modifying the innerHTML of the element. The innerHTML and all properties of the element associated with the behavior are directly accessible by the behaviors script. This is because the element with the behavior is represents the current scope for all immediately executed code. A behavior can therefore replace the contents of the element as follows:

  // Assigning to the innerHTML of the element
  // the behavior is being applied to.
  innerHTML = "Hello World"

Getting quickly into the details of our htmlArea behavior, we provide the complete source code below. When examining the code, notice that we are replacing the contents of the element with the editor user-interface and HTMLArea. Since we want to later program the HTMLArea, we give the HTMLArea a unique id. To generate the unique ID, we use a new method, uniqueID, that automatically finds an unused ID for use by our behavior. With this ID, we are able to access and attach to the events of the newly inserted HTMLArea element:

<scriptlet>
  <implements id="behavior" type="behavior" default/>
  <script language=JScript>

  // Hook the click event of the span
  attachEvent("onclick", checkFormat)

  function checkFormat() {
    // Get event object from the behavior
    var el = behavior.window.event.srcElement
    // If the src is an input button, format the text appropriately
    if (el.tagName=="INPUT")
      format(el.getAttribute("_format"),el.getAttribute("_format2"))
  }

  function format(cmd) {
    // Make sure there is a selection to format
    if (behavior._selection!=null) {
      behavior._selection.select()
      // ExecCommand formats the selected text
      if (arguments[1]==null)
        behavior._selection.execCommand(cmd)
      else 
        behavior._selection.execCommand(cmd,true,arguments[1])
      behavior._selection.select()
    }
  }

  function cacheSelection() {
    // Cache the selected textRange
    var temp = behavior.document.selection.createRange()
    var ev = behavior.window.event
    if ((ev.type!="select") || (temp.text!=""))  
      behavior._selection = temp
  }

// Output the new UI
var str = "<TABLE><TR><TD><TD align=center>"

// For each command, we store formatting information for the
// execCommand in two custom properties
str+= "<INPUT TYPE=\"Button\" VALUE=\"B\" _format=\"bold\" >"
str+= "<INPUT TYPE=\"Button\" VALUE=\"I\" _format=\"Italic\" >"
str+= "<INPUT TYPE=\"Button\" VALUE=\"U\" _format=\"Underline\"> "
str+= "<INPUT TYPE=\"Button\" VALUE=\"|==\" _format=\"JustifyLeft\">"
str+= "<INPUT TYPE=\"Button\" VALUE=\"=|=\" _format=\"JustifyCenter\">"
str+= "<INPUT TYPE=\"Button\" VALUE=\"==|\" _format=\"JustifyRight\">"
str+= "</TR><TR><TD ID=blocks>"
for (var i=1; i<=6; i++)
 str+="<INPUT TYPE=\"Button\" VALUE='H" + i + "' _format=\"FormatBlock\" _format2=\"\"><BR>"
str+="<INPUT TYPE=\"Button\" VALUE=\"P\" _format=\"FormatBlock\" _format2=\"<P>\"><P>"
str+="<INPUT TYPE=\"Button\" VALUE=\"OL\" _format=\"FormatBlock\" _format2=\"<OL>\">><BR>"
str+="<INPUT TYPE=\"Button\" VALUE=\"UL\" _format=\"FormatBlock\" _format2=\"<UL>\"><P>"
str+="<INPUT TYPE=\"Button\" VALUE=\">>\" _format=\"Indent\"><BR>"
str+="<INPUT TYPE=\"Button\" VALUE=\"<<\" _format=\"OutDent\">"
str+="</TD><TD>"

// uniqueID is a new method that generates a page-wide unique ID for the element
myID = uniqueID
str+="<HTMLAREA STYLE=\"width: 300; height: 100%\" ID=" + myID + ">"
str+="</HTMLAREA></TD></TR></TABLE>"

// Write the contents into the page
innerHTML = str
// Hook the events of the HTMLArea. 
// The uniqueID generated above is used to locate the element on the page
behavior.all[myID].attachEvent("onselect",cacheSelection)
behavior.all[myID].attachEvent("onmouseup",cacheSelection)
behavior.all[myID].attachEvent("onkeyup",cacheSelection)

</script>
</scriptlet>

We hope this provides you with a very quick introduction to DHTML behaviors. We recommend you check out Microsoft's Sitebuilders for more information on behaviors. Also, keep checking back with us as we are planning to cover behaviors in more detail with more examples in the future (for example, we want to convert all our scriptlet examples to behaviors. any volunteers to help?).

Editor Improvements

We wrote the editor to show off the HTMLArea element and DHTML Behaviors. However, the editor is still not ready for primetime. To make the editor really useful and even more down-level friendly, we would add the following two improvements:

  1. Extract any default value in the TextArea and insert it into the HTMLArea element. Currently the HTMLArea is created with no value.
  2. Insert a HTMLArea element that is the same size as the TEXTAREA element,
  3. Only show the editing user-interface when the user is inside the edit box. Using CSS-P the user-interface can be designed to not effect the flow of the document.

This concludes our first article on Internet Explorer 5.0 Developer's Preview. Be sure to visit our feedback discussion forum and let us know if you want us to continue with more Internet Explorer 5.0 coverage.

Discuss and Rate this Article