Preamble:
Wow, it's been a long time since we last met...that dreaded work/personal life took over and suddenly scratching out AF() articles fell way down on the priority list.... Let's get back to it!
Introduction:
The previous two AIJS episodes focused on the standard IE behaviors. While they're great and all, we're all coders right? We like to build things with our bare hands! The boys and girls in Redmond didn't let us down...we have Attached Behaviors to satisfy this thirst. Back when IE 5 came out, these were called DHTML Behaviors, then in a moment of supreme revision, they renamed them Attached when they intro'd the other Behavior, the Element Behavior (more about those in the next episode). Remember, for the most comprehensive (and it ain't much) coverage for behaviors, I look to MSDN.
Attached Behaviors let us write a small (or large) amount of code (JScript naturally -- but OK, OK, it could be VBScript) and attach it to a standard HTML element. Thus the name. First we'll look at the attaching, then we'll look at the behavior code itself.
Attaching an Attached Behavior
When you create an Attached Behavior, you will create a file with the extension .htc. Simple enough. For example, I've created a Behavior called a_datebox.htc. (I prefix my Attached Behaviors with a_ and my Element Behaviors with e_, that's just how I am...). To attach this behavior you need to have to have something to attach it to. This something lies within some HTML page, for this example, an input box. For the rest of this article, I'll refer to this guy as the consuming page or the consumer. Attached Behaviors can be attached to any valid HTML element on the consumer, we are not limited to input boxes. We do the attaching two ways:
1. We attach it directly to the HTML using the style attribute, like <input id='txtWhatEver' style='behavior:url(a_datebox.htc)' / >. All normal pathing rules apply, this path assumes that the behavior is a peer of the HTML file.
2. We attach it to a style class, and then make the HTML have that class, like <input id='txtWhatEver' class='datebox' />. Where we have a css class of .datebox{behavior:url(a_datebox.htc)}. We can have this in a local style section in the head or on an external css file, it's however you do your style....
At this point (after page-load), that HTML input box has it's core functionality extended using the Attached Behavior, a_datebox.htc. So what exactly is a_datebox.htc? Glad you asked.
Our Requirements
Imagine a crazy situation like this: You're building an HTML based data-entry system. The users are unable to quickly convert the numeric looking value of 12/2004 into something meaningful, and would like to see something more obvious like December, 2004. And in the effort of eliminating extraneous keystrokes, they only want to enter 122004, then have that "box thing" "magically" convert it to December, 2004. Oh yeah, one other thing, the middleware/database folks don't want no stinkin' crazy date, they want a "real" date, like "12/1/2004". Hmmm, I can do this. One last thing...we'll have a bunch of these all over various applications...okay? So naturally this is a job for an Attached Behavior!
I'm not going to dive too deep into the magic of date converting and stuff like that...that's not unique to Behaviors. Instead, let's focus on what makes Behaviors unique and how they are laid out.
An Attached Behavior
As I stated earlier, an Attached Behavior is just a file, mostly script, saved with a .htc extension. It's layout is pretty specific. It needs to look something like:
<public:component name="a_datebox" urn="">
--- interface declaration
--- script block
</public:component>
OK, so I said it has to be pretty specific...I lied a little. There seems to be some flexibility in the layout. For instance, these work great if they are valid XML, so you escape out your script block with <[[CDATA tags. But wait, if you don't, they work fine. Some people hang the script block on the end, after the close tag, </public:component>. So while the major components need to be there, their order seems to be pretty malleable. I'm not so hung up on structure that I miss the best part of Behaviors: That nifty XML section at the top called the Interface Declaration.
The what? The Interface Declaration?
When I made the leap from "real" programmer to "just a web" guy (kidding of course...), I missed some of the more niftier things that I'd grown accustomed to. One of them was when I created a JScript object I had lost the visual distinction between the public and the private interface. Behaviors has nicely solved that. Here is a link to the actual source behavior, a_datebox.htc. (You'll get a Save/Open dialog here, if you want it local, hit open and IE will throw it into your favorite editor...) At the top of every Behavior, you must have the following stuff:
<public:component name="a_datebox" urn="">
<public:property name="xvalue" get="GetValue" put="PutValue" />
<public:property name="yvalue" get="GetYValue" put="PutYValue" />
<public:property name="text" get="GetText" />
<public:property name="error" get="GetError" />
<public:property name="longmonth" get="GetLongMonth"
put="PutLongMonth" value="false" />
<public:property name="monthnumber" get="GetMonthNumber"
put="PutMonthNumber" value="false" />
<public:property name="errortext" get="GetErrorText" />
<public:method name="ClearOut" />
<public:method name="InsertDate" />
<public:attach event="onblur" for="element" handler="OnBlur" />
<public:attach event="onfocus" for="element" handler="OnFocus" />
<public:event id="evt1" name="ondateblur" />
<public:event id="evt3" name="onerror" />
<public:event id="evt4" name="ondatefocus" />
We've got 5 major sections here, and let's look at them. First, <public:component name="a_datebox" urn="">, the header. The header identifies the .htc. You have a name attribute and urn attribute. The real use for the name is that if the HTML element has more than one Behavior attached to it, you can uniquely id the Behavior. On the previous example, txtWhatever.a_HTC1.dosomething() is different than txtWhatever.a_HTC2.dosomething(). The urn furthers that clarification, specifically for events. Take a gander at MSDN for the full breakdown on urn. I've never had to use 'em! In fact, there are a bunch of additional attributes that you can put all over this section, what I'm showing you is the bare minimum and like most things, that's enough to get virtually everything done.
Next we break into the 4 real interface sections: properties, methods, events and custom events. Properties are cool because they support real get/put operations. What? Property set/get in JScript? That's right. Looking at our datebox we have a longmonth property that is read/write, so we have a function for get and a function for put. Here's how each of them look:
function GetLongMonth()
{
return mUseLongMonth;
}
function PutLongMonth(vValue)
{
mUseLongMonth = vValue;
}
A couple of things to explain. The variable mUseLongMonth is declared globally. For anyone who has done other similar stuff will immediately recognize mUseLongMonth as a module level variable and my naming convention has always started them with an "m". So when the Get is called, the .htc returns the value in mUseLongMonth. When the Put is called, we save the inbound value vValue into the module level variable. vValue is what I use for this, any name will suffice. In addition on the declaration line, I've added value="false". This is one of the ways to set the default value for this property, another would be to set the module level variable to false, var mUseLongMonth = false. Now another cool part of properties is how they are called on the consuming page. You can set them like this:
<input id='txtWhatEver' style='behavior:url(a_datebox.htc)' longmonth='true' />
or in code, either way works, it just depends on your needs:
txtWhatever.longmonth = true;
In the example interface of a_datebox.htc, .text, .errortext and .error are ready-only properties. Again, just like in them "real" languages, if you have a property with only an accessor, it's read-only. The rest of the properties are read/write.
That's a brief rundown on Properties, let's move on to Methods. Methods are just functions, but these are publicly exposed and can contain parameters. I don't have any here that use parameters and I personally set Properties first then do bare Methods, but that's just a style issue. Real simple: on the consuming page txtWhatever.ClearOut() will run the ClearOut() function inside the Behavior. Standard JScript functionality here, not a lot of magic. The method does have to be declared in the Behavior interface, so if you call txtWhatever.doStuff() and doStuff isn't declared as a public:method, your consuming page will error...even if the Behavior has a method named doStuff() . That's true public vs. private interface stuff in action! If a method isn't declared in the interface, it is by definition a private method.
Last are events and I divide them into two sections, standard and custom. Standard is pretty simple. In our example <public:attach event="onfocus" for="element" handler="OnFocus" /> we are declaring to the HTML control that when you receive focus event="onfocus" send control to the local function OnFocus, the event's handler. Based on a very unscientific test and my poor memory, I'm pretty sure that the consuming page's event handler goes first if you have a handler on the page that matches an event in the .htc. Again, your results may vary. Custom events are very cool and very powerful. And very simple: the .htc throws the event whenever you dictate and if the consuming page is "listening", the consuming page can do something with it. For our example I've added <public:event id="evt3" name="onerror" /> to be thrown when what was entered in the input box is wrong (bad date, alpha characters, stuff like that). Very simply, where ever in the .htc code I have var bad = evt3.fire ();, the event fires. (bad and evt3 are my names, no magic here.) Now out on the consuming page, if I have <input id='txtWhatEver' style='behavior:url(a_datebox.htc)' longmonth='true' onerror='MyErrorHandler();' /> , the local function MyErrorHandler will be called, whenever the Behavior code throws the error. So if you want to have an alert, or change the background color or reformat their hard drive, the Behavior owns the conditions, but the consumer owns the response to the conditions...just the way they teach in them Computer Science classes that those "real" programmers take!
OK, we got interfaces, property set/gets, public/private methods, custom events and module level variables...what, did JScript take a page from a "real" language like VB or C#? I'd answer yes. What you are building with this little .htc file is a light weight COM object. At least that's what it is to IE. Those clever devils at Microsoft came up with a way for you to build ActiveX (COM) objects, out of script and have IIS & IE work them. I've said it before and I'll say it again: Behaviors are just another example of how Microsoft makes life much easier for the developer. They are also a prime example of Microsoft doing something cool and nearly ignoring it...but let's move on.
To see this behavior in action, fire up my example page. I try to build this page with all of my Behaviors, mainly so I can remember what they do. Originally it was done with the hope that other developers on my team would use these common controls in their projects, a dream that I can say did not really succeed, but that's another very long and very boring story. Somewhere in the bowels of my hard drive, I've got an unfinished C# project that generates the basic .htc code, setting up all the properties, methods & events, stubbing out everything nicely. Plus it generates this test HTML file....at least that was the plan. I don't think I ever finished it. But I've also got a special treat that is similar to this idea, though not nearly as exotic. Once upon a time there was a book, "Windows Web Scripting" by Dan Heflin & Todd Ney. I mentioned this as one of the few books I've seen with anything about Behaviors. These guys wrote an HTA (HTML Application, coming soon...) that does this. I've included it in this file. There's a couple other interesting Attached Behaviors too. Disclaimer: these files are not mine, I'm not claiming they are. No, that's not a virus disclaimer (they are clean, I assure you..) but it's a noble attempt to give credit where credit is due.
An Aside:
I was looking around MSDN while I was writing this and I've noticed that they are referring to DHTML Behaviors as DHTML Behaviors and Element Behaviors as Element Behaviors. What happened to the name Attached? I have no idea. Attached always seemed to be a better name, since a DHTML Behavior doesn't have any HTML (per se) in it... Which is a hint to anyone about what the difference between Attached/DHTML & Element Behaviors.
The Finish Line:
Whew! That was a whirlwind tour of Attached Behaviors. I went fast and feel like I didn't dive into much detail. There are a lot of attributes and subtle timing issues like "is the Behavior loaded?" that I'm just letting the natural flow take care of. Play around, dig into MSDN or yell at me. If you are a code scrounger, I hope I've given you plenty to dig around in. If you're not, or think I can help you dive into it a little deeper...let me know. Next time I'll hit the Element Behaviors, a natural evolution of the Behavior concept from Microsoft.
If anyone has any comments or questions, email me at ms_@_mybluecoat.com (remove the underlines first...) or use the nifty comment deal that Foo has added to AlphaFilter...and keep those cards and letters coming.