jack.slocum
4 Feb 2007, 5:33 AM
This will eventually find it's way to a blog post after the release, but in the meantime I am looking for feedback.
Currently on Element and EventManager, there are plethora of functions for attaching event handlers. IMO, this not only adds redundancy in the code, but makes the API cluttered. For the new release, there will be a new syntax that I believe will be both more powerful and easier to use. The the idea is to combine all the various listener functions into 1 function addListener() with the standard shorthand of on().
There are helper functions in the 1.0 compatibility file so existing code doesn't break.
Using the new syntax, different types of handlers can be combined and new options could be introduced without changing the API.
Also, all handlers would be called with 3 arguments: the standard "e" argument (like now) plus the target of the event, and the options object passed in. This way you can pass custom arguments to handlers:
Let's looks at some examples, assuming el is an Ext.Element and this.onClick is a handler function.
Custom Args are slightly different
function onClick(e, target, options){
alert(options.foo);
}
el.on('click', this.onClick, this, {foo: 'wtf'});
Note: In the new version, all events will be normalized by default (unlike now). Although the example below sets normalized to false, I really can't imagine anywhere you would want to do this in real code and it would only be there for backwards compatibility. Also, in the new version, you do not have to keep track of "wrappedFn" in order to remove normalized listeners. It handles this automatically.
Because I am lazy, all the examples below use the shorthanded "on()". ;)
Standard YUI Handlers
Current:
el.addListener('click', this.onClick, this, true);
el.on('click', this.onClick, this, true);
New:
el.on('click', this.onClick, this, {normalized: false}); <-- just for backwards compat
Ext Normalized Events
Current:
el.addManagedListener('click', this.onClick, this, true);
el.mon('click', this.onClick, this, true);
New:
el.on('click', this.onClick, this);
Delayed Listeners (delayed event firing)
Current:
el.delayedListener('click', this.onClick, this, true, 250);
New:
el.on('click', this.onClick, this, {delay: 250});
Buffered Listeners (buffers an event so it only fires once in the defined interval).
Current:
el.bufferedListener('click', this.onClick, this, 100);
New:
el.on('click', this.onClick, this, {buffer: 100});
"Handler" Listeners (prevents default and optionally stops propagation).
Current:
// prevent default
el.addHandler('click', false, this.onClick, this, true);
// prevent default and stop propagation
el.addHandler('click', true, this.onClick, this, true);
New:
// prevent default
el.on('click', this.onClick, this, {preventDefault: true});
// prevent default and stop propagation
el.on('click', this.onClick, this, {stopEvent: true});
// only stop propagation (not supported before)
el.on('click', this.onClick, this, {stopPropagation: true});
New Options
I was able to add the following options without additional functions and keeping the API clean. This is really the best part about the new syntax.
One time listeners removed automatically after the first fire:
el.on('click', this.onClick, this, {single: true});
Automatic event delegation!
el.on('click', this.onClick, this, {delegate: 'li.some-class'});
Combining Options
Using this new syntax, it would also be possible to combine different types of listeners:
// a normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
el.on('click', this.onClick, this, {
single: true,
delay: 100,
stopEvent : true,
forumId: 4
});
Attaching multiple handlers in 1 call
This also opens the door for attaching multiple listeners in one shot, which I really wanted:
el.on({
'click' : {
fn: this.onClick
scope: this,
delay: 100
},
'mouseover' : {
fn: this.onMouseOver
scope: this
},
'mouseout' : {
fn: this.onMouseOut
scope: this
}
});
Or a shorthand syntax:
el.on({
'click' : this.onClick,
'mouseover' : this.onMouseOver,
'mouseout' : this.onMouseOut
scope: this
});
Observable
This new syntax is also supported by Observable. In an effort to clean up the code, all legacy YUI CustomEvent objects (e.g. splitter.onMoved.subscribe(...)) have finally been removed (they were deprecated long ago). If you are using the old deprecated events, your code will break (sorry).
Observable no longer uses YUI CustomEvent object. It uses Ext.util.Event, a new class similar to CustomEvent, but that supports the new syntax, is lightweight and significantly faster. The latest release of YUI included a dual logic in CustomEvent (FLAT/LIST) that slowed it down. There's also a bunch of logic in the CustomEvent constructor that makes them slow to create. Removing them gave a noticeable improvement (there are a lot of events, so any improvement is noticeable).
Component Support
The multi event attachment is also supported by Ext components:
grid.on({
rowclick : function(...){
// do something
},
rowcontextmenu : function(){
// do something else
},
scope: someObject,
arg: someArgument
});
Input Appreciated
I am pretty happy with how it turned out. A unified API for attaching listeners will help newbies getting started, and Ext pros get more done. What do you guys think?
Currently on Element and EventManager, there are plethora of functions for attaching event handlers. IMO, this not only adds redundancy in the code, but makes the API cluttered. For the new release, there will be a new syntax that I believe will be both more powerful and easier to use. The the idea is to combine all the various listener functions into 1 function addListener() with the standard shorthand of on().
There are helper functions in the 1.0 compatibility file so existing code doesn't break.
Using the new syntax, different types of handlers can be combined and new options could be introduced without changing the API.
Also, all handlers would be called with 3 arguments: the standard "e" argument (like now) plus the target of the event, and the options object passed in. This way you can pass custom arguments to handlers:
Let's looks at some examples, assuming el is an Ext.Element and this.onClick is a handler function.
Custom Args are slightly different
function onClick(e, target, options){
alert(options.foo);
}
el.on('click', this.onClick, this, {foo: 'wtf'});
Note: In the new version, all events will be normalized by default (unlike now). Although the example below sets normalized to false, I really can't imagine anywhere you would want to do this in real code and it would only be there for backwards compatibility. Also, in the new version, you do not have to keep track of "wrappedFn" in order to remove normalized listeners. It handles this automatically.
Because I am lazy, all the examples below use the shorthanded "on()". ;)
Standard YUI Handlers
Current:
el.addListener('click', this.onClick, this, true);
el.on('click', this.onClick, this, true);
New:
el.on('click', this.onClick, this, {normalized: false}); <-- just for backwards compat
Ext Normalized Events
Current:
el.addManagedListener('click', this.onClick, this, true);
el.mon('click', this.onClick, this, true);
New:
el.on('click', this.onClick, this);
Delayed Listeners (delayed event firing)
Current:
el.delayedListener('click', this.onClick, this, true, 250);
New:
el.on('click', this.onClick, this, {delay: 250});
Buffered Listeners (buffers an event so it only fires once in the defined interval).
Current:
el.bufferedListener('click', this.onClick, this, 100);
New:
el.on('click', this.onClick, this, {buffer: 100});
"Handler" Listeners (prevents default and optionally stops propagation).
Current:
// prevent default
el.addHandler('click', false, this.onClick, this, true);
// prevent default and stop propagation
el.addHandler('click', true, this.onClick, this, true);
New:
// prevent default
el.on('click', this.onClick, this, {preventDefault: true});
// prevent default and stop propagation
el.on('click', this.onClick, this, {stopEvent: true});
// only stop propagation (not supported before)
el.on('click', this.onClick, this, {stopPropagation: true});
New Options
I was able to add the following options without additional functions and keeping the API clean. This is really the best part about the new syntax.
One time listeners removed automatically after the first fire:
el.on('click', this.onClick, this, {single: true});
Automatic event delegation!
el.on('click', this.onClick, this, {delegate: 'li.some-class'});
Combining Options
Using this new syntax, it would also be possible to combine different types of listeners:
// a normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
el.on('click', this.onClick, this, {
single: true,
delay: 100,
stopEvent : true,
forumId: 4
});
Attaching multiple handlers in 1 call
This also opens the door for attaching multiple listeners in one shot, which I really wanted:
el.on({
'click' : {
fn: this.onClick
scope: this,
delay: 100
},
'mouseover' : {
fn: this.onMouseOver
scope: this
},
'mouseout' : {
fn: this.onMouseOut
scope: this
}
});
Or a shorthand syntax:
el.on({
'click' : this.onClick,
'mouseover' : this.onMouseOver,
'mouseout' : this.onMouseOut
scope: this
});
Observable
This new syntax is also supported by Observable. In an effort to clean up the code, all legacy YUI CustomEvent objects (e.g. splitter.onMoved.subscribe(...)) have finally been removed (they were deprecated long ago). If you are using the old deprecated events, your code will break (sorry).
Observable no longer uses YUI CustomEvent object. It uses Ext.util.Event, a new class similar to CustomEvent, but that supports the new syntax, is lightweight and significantly faster. The latest release of YUI included a dual logic in CustomEvent (FLAT/LIST) that slowed it down. There's also a bunch of logic in the CustomEvent constructor that makes them slow to create. Removing them gave a noticeable improvement (there are a lot of events, so any improvement is noticeable).
Component Support
The multi event attachment is also supported by Ext components:
grid.on({
rowclick : function(...){
// do something
},
rowcontextmenu : function(){
// do something else
},
scope: someObject,
arg: someArgument
});
Input Appreciated
I am pretty happy with how it turned out. A unified API for attaching listeners will help newbies getting started, and Ext pros get more done. What do you guys think?