[Prototype-core] More Event Handling Propositions

2007-02-08 Thread Ken Snyder

Colin Mollenhour wrote:
 But, I don't think the overhead of a bind call and the storage of an
 object with a function for EACH event observed is justifiable, just to
 have the convenience of:
   handle.stopObserving();
 rather than:
   Event.stopObserving(handle);
Colin, your patch is superb.  Thanks!

I completely agree. Memory usage and speed far outweigh the syntactic 
sugar gained by returning an object.

I've been doing a lot of research on the subject of Event registration.  
It made me question: do we really need to detach listeners in IE on 
unload?  Unless anyone knows for sure, I plan to test.  It might make 
sens to turn off caching by default _if_ detaching IE listeners on 
unload is unnecessary.  Also, I think we can remove the try-catch blocks 
on lines 116 and 125 of your patch because exceptions should never be 
generated (see 
http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/events.html#Events-EventTarget-addEventListener)--perhaps
 
more testing...

--Ken


P.S. Just to share my research, let me summarize the Event registry 
issues: (via http://www.quirksmode.org/js/events_advanced.html and others)

1. In IE, this inside the attached function refers to window.
2. In IE, events always bubble.
3. The new W3C Event attribute eventListenerList is not yet 
implemented in any browser.
4. Legacy browsers (NS4,IE4,IE5/Mac) do not support multiple event 
registration.
5. Some browsers prefer keydown over keypress.
6. Safari will return text nodes as targets.


1. Prototype cleverly circumvents the first: Prototype provides 
Function.bindAsEventListener() and Event.element(event) to reliably find 
the target element.  I say clever because other solutions (such as 
attaching the function to the element: 
http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html) 
end up causing more problems than they solve.  Incidentally, I find that 
passing arguments to Function.bind() and Function.bindAsEventListener() 
often makes using Event.element() unnecessary.

2. Prototype solution: In both models Event.stop() stops bubbling if 
requested

3. Prototype solution: To be addressed through caching (i.e. Colin's patch!)

4. Prototype solution: Do not support legacy browsers (hallelujah)

5. Prototype solution: Simple detection

6. Prototype solution: none.  Should we patch Event.element() with an 
additional line?

Event.element = function(event) {
  var target = event.target || event.srcElement;
  return target  target.nodeType == 3 ? target.parentNode : target;
}

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Prototype: Core group.
To post to this group, send email to prototype-core@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-core?hl=en
-~--~~~~--~~--~--~---



[Prototype-core] More Event Handling Propositions

2007-02-07 Thread Ken Snyder

Along with all this Event handling discussion, I'd like to throw in my 
ideas for a rewrite of Event.observe et al before I test and submit as a 
patch.  My proposed changes allow the traditional Event.stopObserving() 
and simplify some of the current messaging ideas.  Please provide feedback.

Consider this API:

var myHandle = Event.observe(myElement, 'click', myFunction);
myHandle.stopObserving();



// with support for the traditional Event.stopObserving()
Event.stopObserving(myElement, 'click', myFunction);


Draft suggestion below.  One thing I'm also considering is making the 
handle's stopObserving function remove its own entry from the 
Event.observers array, but that isn't technically necessary.

--Ken Snyder



Object.extend(Event, {
//...
// the cache of observers
observers: [],
//
// replacements for observe, stopObserving, unloadCache with 
enhanced functionality
//
observe: function( element, event, observer, useCapture ) {
element = $(element);
useCapture = useCapture || false;
element[Event._addFnName](Event._eventName(event), observer, 
useCapture);
var handle = {
event: event,
observer: observer,
useCapture: useCapture,
stopObserving = function() {
try { 
element[Event._removeFnName](Event._eventName(event), observer, 
useCapture); } catch(e) {}
}
}
Event.observers.push(handle);
return handle;
},
stopObserving: function( element, event, observer, useCapture ) {
element = $(element);
useCapture = useCapture || false;
Event._stopObservations(function(handle) {
return handle.element==element  handle.event==event  
handle.observer==observer  handle.useCapture==useCapture;
});
},
unloadCache: function() {
Event.observers.invoke('stopObserving');
Event.observers = null;
},
//
// New methods
//
// New method: cancel all the observers associated with a particular 
element for a particular event
stopObservingElementEvent: function( element, event ) {
element = $(element);
Event._stopObservations(function(handle) {
return handle.element==element  handle.event==event;
});
},
// New method: cancel all the observers associated with a particular 
element
stopObservingElement: function( element ) {
element = $(element);
Event._stopObservations(function(handle) {
return handle.element==element;
});
},
// internal function to cancel one or more observers
_stopObservations: function(partitioner) {
var handles = Event.observers.partition(partitioner);
if( handles[0].length ) {
handles.invoke('stopObserving');
Event.observers = handles[1];
}
}   
};
// pre-define cross-browser function and event naming
if( window.addEventListener ) {
Event._addFnName = 'addEventListener';
Event._removeFnName = 'removeEventListener';
if( navigator.appVersion.match(/Konqueror|Safari|KHTML/) )
Event._eventName = function(name) { return name=='keypress' ? 
'keydown' : name; };
} else {
Event._eventName = function(name) { return name; };
}
} else if( window.attachEvent ) {
Event._addFnName = 'attachEvent';
Event._removeFnName = 'detachEvent';   
Event._eventName = function(name) { return 'on'+name; };
// garbage collect for IE
window.addEventListener('onunload', Event.unloadCache);
}

Improvements:
- Browser capability is detected only once
- Has a simpler API
- Gives a simple way to cancel all observers for a particular element or 
element-event combination
- It is simple to cache all observers associated with a particular 
widget and stop all with widgetHandles.invoke('stopObserving');
- It is easily extendable (i.e. new methods such as 
Event.isObservedElement() and Event.isObservedElementEvent())



--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Prototype: Core group.
To post to this group, send email to prototype-core@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-core?hl=en
-~--~~~~--~~--~--~---