Events in JavaScript or Dojo based applications are essential to making applications work. Connecting an event handler (function) to an element or an object is one of the most common things you will do when developing applications using Dojo. Dojo provides a simple API for connecting events via the dojo.event.connect() function. One important thing to note here is that events can be mapped to any property or object or element. Using this API you can wire your user interfaces together or allow for your objects to communicate. The dojo.event.connnect() API does not require that the objects be Dojo based. In other words, you can use this API with your existing interfaces.
dojo.event.connect has multiple function signatures, but one of the simplest is:
dojo.event.connect(srcObj, "srcFunc", targetFunc);
The arguments are the source object, the source function (in quotes) and the target function reference or anonymous function.
Here we have a DOM node called mylink, and whenever that DOM node is clicked myHandler will be called:
var link = dojo.byId("mylink");
dojo.event.connect(link, "onclick", myHandler);function myHandler(evt) {
alert("dojo.connect handler");
}
Above the "onclick" property of link element is connnected to the function myHandler.
But what if we don't want to set up a named function for the event handler? No problem:
var link = dojo.byId("mylink");
// connect link element 'onclick' property to an anonymous function
dojo.event.connect(link, "onclick", function(evt) {
...
});
The example above shows how an anonymous function can be mapped to the "onclick" property of a link element with an existing in-lined DOM 1 style handler connected to using the "onclick" attribute of the element.
So far, though, we're not doing anything that can't be done by setting the onclick property of the DOM Node. But what about attaching a method of an object to a DOM Node's event handler? Normally, you'd have to do something like:
var handlerNode = document.getElementById("handler");
handlerNode.onclick = function(evt){
object.handler(evt);
};
Dojo simplifies it to:
var handlerNode = document.getElementById("handler");
dojo.event.connect(handlerNode, "onclick", object, "handler");
This connect() call ensures that when handlerNode.onclick() is called, object.handler() will be called with the same arguments. Language limitations of JavaScript make it impossible to pass in the object and function name together, however separating them into an object reference and function name isn't difficult.
So we've seen that connect() can handle DOM events, but what about that more expansive view of events that was mentioned earlier? To demonstrate, lets define a simple object with a couple of methods:
var exampleObj = {
counter: 0,
foo: function(){
alert("foo");
this.counter++;
},
bar: function(){
alert("bar");
this.counter++;
}
};
So lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:
dojo.event.connect(exampleObj, "foo", exampleObj, "bar");
Now calling foo() will also call bar(), thereby incrementing the counter twice and alerting "foo" and then "bar". Any caller that was counting on getting the return value from foo() won't be disappointed. The source method should behave just as it always has. On the other hand, since there's no explicit caller for bar(), it's return value will be lost since there's no obvious place to put it.
We've also inadvertently demonstrated that connect() takes variable forms of arguments. So far, it's correctly handled:
This is par for the course when using connect(). Since it is used in so many places, for so many things, and in so many ways, connect() does a lot of checking and normalization of it's arguments. The connect method tries to disambiguate the types of the positional parameters based on usage. Some common usages are:
The first paramether is adviceType ("after" and "before") and is optional. If it is not supplied then it defaults to "before". In the above example, adviceType was not provided and so the default, in this case "before" is used.
srcObj - the scope (scope1) in which to locate/execute the named srcFunc. This is also optional and if it is not supplied then Dojo assumes the global object.
srcFunc - the name of the function to connect to. In the above examples it is "globalFunctionName2" or "functionName2". This is in conjunction with the srcObj parameter. Dojo will look for a function, srcFunc, in srcObj.
adviceObj - scope (scope 2) in which to locate/execute the named adviceFunc. Again this parameter is optional and if not supplied Dojo will assume the global object.
adviceFunc - name of the function ("globalFunctionName1" or "functionName1") being conected to srcObj.srcFunc
There's one more modifier up the sleeve of connect()/kwConnect(); delayed calling. The delay property in kwConnect (the 9th positional parameter for connect) is a delay in milliseconds for those platforms that support it (all browsers do).
The last problem worth mentioning is circular connections. Circular connections can occur when (perhaps even indirectly) a listener also calls the function it's listening to. The good news is that in a JavaScript interpreter, this will pretty quickly yield an exception of some sort. "Too much recursion" is a tip off that you've hit this problem. Debugging circular connections can be opaque, but tools like Venkman help.