NinJa Event Model

The event model used in HTML and JavaScript leans itself to the ability to attach function calls to specific user driven actions on the page. Unfortunately, the event model used is sometimes difficult to understand. How do you add more events? How do you remove events? How do you reference the element that was attached to the event?!

Enter NinJa's event model. NinJa wraps the entire DOM event model in a thin layer that provides ways to add, remove, traverse, enable and disable specific functions based on a single event.

First off, an Event is a special type of extended ObservableArray. It provides all the same functionality of a normal Array: Add, Insert, Remove, Clear, etc, while tying all the functions to a specific user event. Let's look at a simple example:

Page.OnLoad.Add(function () {
    Page.AddElement(new Span("Me first!"));

Page.OnLoad.Insert(0, function () {
    Page.AddElement(new Span("No no, me first!"));

This highly useful example </sarcasm> demonstrates how the event model works. Events accept one of two types of objects: functions (or a reference to a function) and Delegates (more on those later). The first bit of code simply adds an anonymous function. The second piece of code adds another function to the event list, however, it inserts it at index 0, forcing the first anonymous function to be shifted down one. Once Page.OnLoad fires, "No no, me first!" will appear before "Me first!" since the second anonymous function appears first in the event thanks to the Insert function. We can also Remove, Clear, etc to get ride of functions tied to an event.

It's useful to remember that events that are attached via event attributes (onload="<code here>" for example) always appear first in the event. They can, however, be removed and referenced as an anonymous function, so calling Clear on an event will remove all functions, including those set in the HTML code.

this and UserEventArgs

So when an event occurs, each function in the array is called. Since the event is attached to a specific element, this always refers to the calling object! If the event was attached to an anchor, this refers to that anchor. But it doesn't refer to the raw HTML node. Instead it refers to the HTML node's representation in NinJa. Huh?! See the tutorial on direct DOM access. Just know that to get the object that had the event attached to it, use this. Remember, you can pass in this as a value to a reference function so that you can get Intellisense support of the object. See below for an example:

    var t = $TextBox(this);
    if(t.Text().length == 0){
        alert("Please provide a value!");

In this piece of code, this will already have the function of Text and Focus on it, but Intellisense has no way of knowing what type of object this will be. That's why we use a reference function and assign it to a variable. Now when we use t, we will get Intellisense support for a TextBox.

Another way we can references the calling object is by using the one parameter passed to functions fired from an event of type UserEventArgs. UserEventArgs provide several pieces of information about the event that was fired: EventType, Sender, etc and ways to override standard event handling. Some events have a specialized UserEventArgs, such as KeyEventArgs and ScrollEventArgs. These provide extra information about their specific event type.

UserEventArgs has a Sender field on them that tells what object contained the event. They also provide two ways to short circuit an event: CancelEvent and IsHandled

Setting CancelEvent to true will prevent any other functions from firing in the event. Remember that functions are called in the order that they appear in the Event object, so if the function is first and is set to Cancel the event, no other function will be fired. The default browser behavior, however, will still execute.

Settng IsHandled to true will prevent bubbling of the event and the default handling of the event is canceled out. This can be used to prevent hyper links from taking the user to a page or prevent a submit button from submitting the form.

Finally, this event model is applied to all events in NinJa, but UserEventArgs is used specifically for user driven events for dom elements. All elements have the same basic events:

  • OnClick
  • OnContextMenu (right click)
  • OnDoubleClick
  • OnKeyDown
  • OnKeyPress
  • OnKeyUp
  • OnMouseDown
  • OnMouseMove
  • OnMouseOver
  • OnMouseOut
  • OnMouseUp
  • OnMouseWheel

Other elements have specific events tied to them, such as OnTextChanged for text boxes and OnFocus for form inputs and anchors. See documentation for specific elements for more information.


Ever add a function to event, only wishing you could pass in parameters? Sure, you could add an anonymous function and just call the function you want with the parameters you need, but that's kind of annoying. What if you simply want to disable a function in the event rather than completely remove it from the event? Enter delegates.

Delegates aren't the same as they are in .NET, but they provide a similar purpose. They allow you fine tune the function calls attached to an event. They are special functions that can be executed on their own or added to events. Let's look an useless example:

Page.OnLoad.Add(new Delegate(alert, null, "Hello world!"));

Pretty useful eh? The constructor for a delegate takes basically 3 pieces of information: the function to call, the object that's calling it and finally any parameters that should be passed to the function. by providing null here, we are saying that there is no calling object (thus, this will refer to the global window object). If we provide a calling object, this will be what it refers to in the function call. The order of the parameters provided after that will be what's passed to the function. So this is the equivalent of calling alert("Hello world!").

Ok ok, so that's a roundabout way of calling a function, buddy. This seems pretty useless. But what's useful about this is that Delegate is an object with various fields that can be changed. If you want to change what function is called, use the Function field. You can change the parameters by modifying the Parameters array. You even disable the specific delegate from firing on events by setting Enabled to false! There's plenty of power in the use of delegates and they're not appropriate for all situations, but I've used them through out the entire framework in order to create some pretty fancy controls.

Last edited Jun 13, 2012 at 4:44 PM by dahrkdaiz, version 2


No comments yet.