Exploring one of MS AJAX’s often overlooked features.
AJAX, ASP.NET, JavaScript By Dave Ward on November 15th, 2007
It’s easy to think of the ASP.NET AJAX framework primarily in terms of the server controls it provides. However, make sure that you don’t overlook the client script helper classes at your disposal. Many of them are very useful and surprisingly powerful.
In the next several posts, I’ll be examining these client side helper classes and hopefully giving you some ideas for more effectively using them.
In this first post, I’m going to take a closer look at $addHandler.
$addHandler’s functionality and syntax
$addHandler is a static, shortcut function for Sys.UI.DomEvent.addHandler. It allows you to attach a client side event handler to any DOM element on your page. You have probably seen it before, but might be surprised by how much power and flexibility it offers just under the surface.
Its syntax is as follows:
Sys.UI.DomEvent.addHandler(element, eventName, handler);
Element accepts a DOM element. This argument is most often provided by using $get() or getElementById(). However, any valid reference to a DOM element may be used.
EventName accepts a string specifying which event you want to handle. It expects a standard DOM event name with the “on” prefix removed. For example, use “click” to handle onclick and use “focus” to handle onfocus.
Handler accepts a reference to the JavaScript function that should be called for this event. On event, this handler function will be called with a single argument of type Sys.UI.DomEvent.
Example #1: That pesky enter key.
The enter key tends to bring considerable nuisance to ASP.NET forms. DefaultButton has helped ease that pain somewhat, but submitting the form isn’t always what the user wants to do when they press enter.
Instead of helping the user accidentally submit a half-completed form, we can easily use $addHandler to intercept the enter key and prevent the form submission altogether.
$addHandler($get('TextBox1'), 'keydown', TextBox1_KeyDown); function TextBox1_KeyDown(evt) { // Check to see if it's the enter key that raised // the keydown event. if (evt.charCode == Sys.UI.Key.enter) { // This prevents the form submission. evt.preventDefault(); // Focus the next TextBox instead, similar // to the behavior expected from tab. $get('TextBox2').focus(); } }
This calls the TextBox1_KeyDown client side function on every key press, as long as TextBox1 is focused. If the key press is the enter key, preventDefault() is called to prevent the form submission that would normally occur. Finally, the next form field is focused.
From the user’s point of view, the enter key functions just like the tab key.
Preferably, you would use more generic code for focusing the next form field and then add the same handler to all but the last form field. The code to do that is beyond the scope of this post, but is readily available via Google.
Example #2: I hit the tab key because I wanted a tab!
Something that has always annoyed me about most textarea input fields on websites is that the tab key switches focus to the next form fields, instead of inserting a tab character. In fact, even the textarea I’m using to write this very post has that infuriating behavior.
This is how you could use $addHandler to fix that:
$addHandler($get('MultiLineTextBox1'), 'keydown', TabExpander); function TabExpander(evt) { // If a tab is received, then insert a tab character // in the HTML element that raised the event. if (evt.charCode == Sys.UI.Key.tab) { evt.preventDefault(); // Code here to insert a \t in evt.target, which is // long, messy, and omitted for brevity. } }
Note the use of Sys.UI.Key in this and the previous example. Key is an enumeration of the ASCII key codes corresponding to various special keys, such as enter, ctrl, shift, delete, etc. Sys.UI.Key.enter evaluates to 13, Sys.UI.Key.tab to 9, and so on.
It’s a bit more typing, but I think the drastic readability improvement is well worth it. For a complete list of Key members, see the Sys.UI.Key documentation.
Example #3: Oops, does this thing have an undo button?
Any application with delete capability inevitably incurs all too many accidental deletions. Sure, you can use a confirmation dialog, but your average user is well conditioned to find the “ok” button and click it as quickly as possible.
How about requiring an unusual gesture to activate the delete function? For this example, let’s use ctrl + click. That’s pretty tough to do accidentally.
$addHandler($get('DeleteButton'), 'click', DeleteHandler); function DeleteHandler(evt) { // If the ctrl key was being pressed when the // the click happened, then proceed. if (evt.ctrlKey) evt.target.value = 'Deleting...'; // Else, use preventDefault to abort the click // and let the user know that they've failed. else { evt.preventDefault(); evt.target.value = 'No secret handshake, no delete.'; } }
Evt.target provides a reference to the DOM object that raised the event. So, in the case of this button, using evt.target.value changes the button’s text. This flexibility opens up nearly unlimited potential for rich UI functionality, with relatively minimal work on our part.
Conclusion
Instead of learning to use $addHandler, you might consider wiring events up by using direct assignment (as seen in tutorials on many popular sites). However, it’s important to understand that doing so will overwrite existing handlers for that element/event combination. Do that throughout your code and you will quickly end up in a situation where debugging is tedious at best.
Additionally, the DomEvent class that $addHandler provides your handler function is a great tool. For example, its charCode property does a great job of abstracting cross-browser key code support. To do this by hand, you would need several more lines of code. It’s often the little things that make your code maintainable, and this is one of them.
If you’re already using ASP.NET AJAX anyway, I think using $addHandler for your client side event wireups is definitely a best practice.
Similar posts
What do you think? Your comments are welcomed.
I appreciate all of your comments, questions, and other feedback, but please try to stay on topic. If you have a question unrelated to this post, I recommend posting on the ASP.NET forums. If you post there and then contact me with a link to the post, I'll try to take a look at it for you.
If you're replying to an existing comment, please use the threading feature. To do this, click the "Reply to this comment" link underneath the comment you're replying to.
Trackbacks
- Daily Dose of Links - 20071116 « Daily Geek Bits
- Nov 17th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, VS 2008, .NET 3.5, IIS7, Silverlight « .NET Framework tips
- HowTo: Microsoft AJAX Client API nutzen | Code-Inside Blog
- All about asp.net » Blog Archive » Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, VS 2008, .NET 3.5, IIS7, Silverlight
- Jan 24th Links: ASP.NET, ASP.NET AJAX, Visual Studio, .NET, IIS - ScottGu's Blog

Comments
Can you do $addHandler(document, “load”, function() { /*page load initialisation here*/ }); ?
Yes, you can do that.
I have a situation where I call a page load handler from a script associated with a master page, then on a specific page based on that master page I need to call a second page load handler. I use the pageLoad() shortcut notation in the master page script. I can’t get either pageLoad() (expected since the composite page would have two pageLoad’s) or $addHandler(document, “load”, mySpecificPageLoad()) to work. I had to resort to my old “addLoadListener” function to add the second page load event handler. Is this as expected?
Try adding this in your master’s pageLoad():
Then, on any page that you need a content level pageLoad(), you can write it in mySpecificPageLoad() and the master will execute it automatically.
Thanks! works great.
Great post Dave. The $addHandler is so beautiful because it is unobtrusive.
Be careful about memory leaks from DOM-JavaScript cycles when using events. It’s imperative that $clearHandlers is called before the DOM element is removed.
You’re absolutely right about that.
I like your post. It’s helpful. Thanks!
I don’t know why, but this sample doesn’t work for me. Each time i receive an error “Microsoft JScript runtime error: ‘this._postBackSettings.async’ is null or not an object”
Try doing a global search in your site’s files for “_postBackSettings.async”. It sounds like there’s a bad script fragment somewhere.
If you can’t find what’s causing it, feel free to email me sample code that reproduces the problem and I’ll take a look.
MANY people have been going crazy over this one. As far as I can tell the exact problem and fix are still unknown, but this post helped me. Look for my comment too, it may work even if your situation is slightly different.
http://www.kerrywong.com/2008/01/21/jscript-exception-in-ajax-control-toolkit
Interesting.
The same end result could also be accomplished by setting a DefaultButton on the <form> element, without having to add a superfluous Panel.
It’s worth noting that using the DefaultButton functionality will weigh your page down a bit. Using it causes a WebResource.axd JavaScript include to be added to your page at runtime.
Nice post!
A couple notes
You can’t use $addHandler for the window.onerror event.
Wiring up events through $addHandler adds a level of indirection to the event firing process. The browser event fires, it’s mapped to an ASP.NET AJAX DomEvent object, then the event handlers wired through $addHandler are executed. I’ve found that wiring up some events, specifically the mousemove event, can seriously degrade browser performance. Wiring to the mousemove event is always expensive, but adding the level of event indirection adds another cost.
Also, the DomEvent object, which is what “evt” is in your examples, doesn’t contain all the properties of the original event. You can access the original event through .rawEvent, though.
Looking forward to the next post!
Wow! Thanks for the helpful article. I am just getting started with ASP.NET Ajax and this is good to know.