Saturday, March 24, 2012

Little addition to $addHandler

Hi guys -

This comes in really handy sometimes. Since standard compliant browsers do not have window.event I emulate it via a 1 line addition (line 16 below) to $addHandler. It comes in really convenient and virtually no overhead.

12var $addHandler = Sys.UI.DomEvent.addHandler = function Sys$UI$DomEvent$addHandler(element, eventName, handler) {3 /// <param name="element" domElement="true"></param>4 /// <param name="eventName" type="String"></param>5 /// <param name="handler" type="Function"></param>6 var e = Function._validateParams(arguments, [7 {name: "element", domElement: true},8 {name: "eventName", type: String},9 {name: "handler", type: Function}10 ]);11 if (e) throw e;1213 if (element.addEventListener) {14 if (!handler._browserHandler) {15 handler._browserHandler = function handler$_browserHandler(e) {16 if (e) window.event = e;17 handler.call(element, new Sys.UI.DomEvent(e));18 }19 }20 element.addEventListener(eventName, handler._browserHandler, false);21 }22 else if (element.attachEvent) {23 if (!handler._browserHandler) {24 handler._browserHandler = function handler$_browserHandler() {25 handler.call(element, new Sys.UI.DomEvent(window.event));26 }27 }28 element.attachEvent('on' + eventName, handler._browserHandler);29 }30 if (!element._events) {31 element._events = {};32 }33 var eventCache = element._events[eventName];34 if (!eventCache) {35 element._events[eventName] = eventCache = [];36 }37 eventCache[eventCache.length] = handler;38}3940

Any chance of getting this included in AJAX? I hate hacking away at AJAX functions.

Hi,

just curious, in which scenarios do you think it could be useful?

Also, the code contains a bug: http://forums.asp.net/thread/1487616.aspx

Basically, when you attach the same handlers to multiple elements, the handler is executed under the scope of the first element that got it attached (due to the handler._browserHander flag).


Hey Garbin -

I am actually neglecting the standard in ignoring parameter (laziness and conciseness more than anything). Here's an example.

var a = 'something';
var handler = Function.createCallback(myFunction, a);
$addHandler(el, handler)

function myFunction(s) {
window.event.returnValue = false;
}

Basically I am too lazy to create a function that accepts both a string and the event so I just use the window.event with the addition of that line. It works best for me since I don't need to use it unless I want it.

Thanks for the bug report I will fix on my side tomorrow but this is one of the reasons I hate overriding AJAX functions since I have to stay up to date... I'd prefer this line to be included in the source. ;) Or maybe one day I'll sit down and fix up my code!


Hello again Garbin -

I came up with a way better scenario than my laziness!!!!

A good justification for including this is DragDropManager in the preview bits. If you look at the handlers they use window._event to pass around the current event.

Is this a better scenario than my laziness? ;)

Alex


The idea is not to reinvent the browser API. Assinging window.event is like adapting FF, for example, to work like IE.

You should just use the event object passed into the handler as a parameter. If you really need the raw event objects because it contains something browser specific, you can use the rawEvent field on it.


Oh and I wouldn't necessarly use the preview bits as a justification... they are preview bits afterall :)

Hi,

yes I remember the DragDropManager. If you browse the code of the slider behavior in the control toolkit, you'll notice that I had to assign the event object just received in a handler, to window._event in order to make it work... not a best practice I think :)


InfinitiesLoop - I understand your point and definitely think staying with the standards is a good thing. Here's my justification for breaking away from this. Since we cannot assign context via $addHandler(s) we would have to assign a value via the DOM element and assign an attribute as a way to pass a context and then retrieve it via event.target.attribute. DOM access is slow and memory bug prone (if what I am trying to pass is a function) - I would need to do clean up and all that comes with it.

What I can do is a create something along the lines of the below to pass along the event and a context.

var $createDomFn = function(instance, fn, context) {
return function(e) {
fn.apply(instance, [ context, e ]);
}
}

I am personally not a fan of this type of solution because I can't use standard Function.createCallback.

I find it way more convenient to be able to access window.event raw event and work with it. If I want to use the DomEvent version (which is not completely cross browser and I've reported this) I can use new Sys.DomEvent(window.event).

Just my 2 cents.


Hi,

could you elaborate on why you can't use Function.createCallback? I think a callback could be the best pattern that fits your scenario.


I would think that any context info your event handler needs that is specifically tied to the dom element, should be on the dom element or associated with a control that is attached to the dom element anyway. If the context isn't specifically tied to the dom element then creating a closure like you demonstrated makes sense, it's the same thing as creating an instance of a class, assigning some state to it, then using one of its methods as the event handler (using Function.createDelegate).

Maybe I'm not understanding exactly what you mean. What would you have done with attachEvent? addHandler has no take-away. Or do you mean you'd like the event info to be available from anywhere in the callstack (like what the drag drop manager is doing)? Isn't better design to have the methods accept parameters for exactly what they need? Its the old global variable issue... you want to limit state to where it is needed. If there's something generic you are calling into that may potentially need the event info but you don't know until runtime, then you can devise a mechanism to store the event object so it can be accessed as needed, but not on the global window object. Maybe a custom event args that is passed down, or some other static... and then, maybe not the entire event object, maybe a high level state object that has already undergone some interpretation.

It may be more convenient to have window.event, I'll give you that... but convenience isn't the only goal of a solid framework.


Garbin - you are right Function.createCallback would fit my scenario (I use my own flavor of this $createFn that combines createDelegate and createCallback), I did not realize it passes the arguments and then pushes the context to the end. Thanks.

InfinitiesLoop - I am not sure I agree with your argument that anything tied to a DOM element should be on the dom element. This can be slower than using a closure and there is clean up involved. I think you are right in saying this is a global variable issue except that I would take this further and say storing anything on a DOM element is essentially the same thing. I personally like to be able to access the window.event object from where I need it, rather than adding an extra variable to the scope of a handler. I also would think that passing around DomEvent in a closure to handlers is more memory and processor intensive than having a global object. I guess it would all depend on the scenario but I can see both ways window.event vs argument event being faster/better in different usages.

I think you may have convinced me to test out which is faster in a scenario where a lot of elements have the same hadler that is fired frequently.

Thanks! Glad to see someone cares about such little things.

No comments:

Post a Comment