Event management

Handling DOM Events

A frequent question on the Haxe mailing list, is how to manage browser events on the Javascript target. HaXe does not provide a native solution to this problem, because event implementations differ between browsers, and a fully cross-browser solution would require typed access to variables that are only available on certain browsers, which is not a desirable solution, because these special variables would then cause runtime errors on browsers that do not support them.

This tutorial describes how you can overcome this limitation - we propose three alternatives: an untyped block; binding to an external library like jQuery or Sizzle; or using a native Haxe event management library called Distill. We will reveal the advantages and disadvantages of each approach.

A word of warning before you begin coding event management systems in the DOM. As with flash, event management is known to be the source of memory leaks and poor performance - some knowledge of the Javascript garbage collector and how it handles the DOM is recommended. For more heartwarmingly detailed information please read: http://javascript.crockford.com/memory/leak.html.

untyped block

It is usually not recommended to use untyped in Haxe, but often it is a necessary evil, especially in situations where a typed solution is impossible, or the native platform contains features that are not available to the Haxe API.

Define the handler function

To add an onclick handler to a DOM element one would first define a click handler:

var clickHandlerFunction : Event -> Void = function ( e : Event )
{
    // clickhandler code ...
}

The parameter type and return type is somewhat up to you, because these variables are passed to your function in a dynamic context. You may, for example, prefer to have your parameter as a Dynamic variable, to give you better access to the event target, or you can type the parameter using a custom Typedef, so that you can access the event object how you intend to use it.

Add a handler

Assuming you have a handle on a DOM Element, you can then do the following:

untyped
{    
    if ( domElem.addEventListener )
    {
        domElem.addEventListener( "click", clickHandlerFunction, false );
    } else {
        domElem.attachEvent( "onclick", clickHandlerFunction, false );
    }
}

(Note: attachEvent event names differ from those used in addEventListener by always having an "on" prefix. E.g. onclick, onmousemove)

Remove a handler

Once you are done with the event handler, you should disconnect the event handler from the DOM tree:

untyped
{    
    if ( domElem.removeEventListener )
    {
        domElem.removeEventListener( "click", clickHandlerFunction );
    } else {
        domElem.detachEvent( "onclick", clickHandlerFunction );
    }
}

Pros

- lightweight solution

Cons

- requires cross-browser knowledge

jQuery / external library

Many people prefer to use an external library, as some external libraries are quite mature and provide advanced event management systems. One way of going about this is to write an extern definition file, and accessing these functions directly - copious information on what extern files are, and how they can be included into Haxe are provided on the js extern page. Another option, if one only needs to access a limited number of functions in the whole library, it may be more desirable to write a simple wrapper function.

haxelib jQuery

Fortunately for Haxe users, there is a jQuery wrapper library in haxelib, saving you from wrapping all of jQuery's functions.

installing

To install you can use the haxelib command line:

haxelib install jquery

Bootsrap

Make sure that the raw jquery library is linked before your application javascript file in the HTML:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" language=
"JavaScript"></script>

declare jquery as an explicit import in your application Haxe file:

import jquery.JQuery;

Binding to a jQuery DOM element

Then, you need to code in a very jQuery-esque fashion:

var domElem = JQuery.Qy( "#myDOMNodeID" );
var clickHandlerFunction = function (e)
{
    // ...
}
domElem.bind( "click", clickHandlerFunction );

A word of warning: all return values are dynamic, and cannot be checked by the Haxe compiler for type safety, and consequently, a lack of type checking may result in more frequent runtime bugs. For smaller programs, this technique is very simple, and provides good results.

Cleaning up

once you are finished with the event handling, ideally you should:

domElem.Unbind( "click", clickHandlerFunction );
There is also another jQuery extern if you want a API closer to jQuery.

Pros

- least amount of work, most amount of features.
- external library is highly optimised and stable.

Cons

- all return values are untyped, Haxe compiler cannot help you fix bugs.

Using Distill

Distill is a small event management system, that lets you avoid an untyped environment as much as possible, without modifying your base Haxe installation. It works on the principle that events bubble up to the top of the DOM hierarchy, and can then be delegated to your Haxe classes.

Installing

To install simply run

haxelib install distill

Bootstrap

In your application you need to import the library:

import Distill;

Howto

Distill works along the concept pioneered by the Dojo framework, which associates a class to a DOM element. This means you have Haxe objects that represent a particular DOM node, and receive all the same events. The association of a DOM node and a Haxe class occurs in HTML markup:

<div name="hxType" class="MyClass">Test clickable element!</div>

You can then create a class "MyClass" to receive the association.

class MyClass
{
    var domNode : HtmlDom;
    function click (e : Event -> Void)
    {
        // ...
    }
}

Your Haxe file does not necessarily need a main method, because "MyClass" will be instantiated on boot. The optional variable "domNode" allows you to reference and manipulate the associated DOM element (the variable can also be typed as FormElement or Image, or whatever type you have associated with this class).

Advanced

The above is all you need to know about distill to get started. There are some more advanced features that are quite useful for interactive widget programming.

Querying

You can query a set of instantiated classes that have been registered in the event management system:

var objects : Array<MyClass> = Distill.getInstance().query("MyClass");

Dynamic DOM association

It's also possible to associate DOM elements after the page has loaded, for example when the DOM structure is specified by an external configuration.

var div = Lib.document.createElement('div');
var myClass = new MyClass();
Distill.getInstance().bind( myClass, div );

A third optional parameter to the "bind" method allows you to disambiguate between multiple instances of the same class, if you require many nodes of the same object, but each node requires its own event handlers.

Pros

- allows better typing
- elicits an environment for widget-based programming

Cons

- heavyweight solution, if you only need an event handler

version #17896, modified 2013-03-11 00:25:55 by skusey