fix(jqLite): allow triggerHandler() to accept custom event

In some scenarios you want to be able to specify properties on the event
that is passed to the event handler. JQuery does this by overloading the
first parameter (`eventName`). If it is an object with a `type` property
then we assume that it must be a custom event.

In this case the custom event must provide the `type` property which is
the name of the event to be triggered.  `triggerHandler` will continue to
provide dummy default functions for `preventDefault()`, `isDefaultPrevented()`
and `stopPropagation()` but you may override these with your own versions
in your custom object if you wish.

In addition the commit provides some performance and memory usage
improvements by only creating objects and doing work that is necessary.

This commit also renames the parameters inline with jQuery.

Closes #8469
This commit is contained in:
Peter Bacon Darwin
2014-08-08 14:10:03 +01:00
parent 37ba3b9493
commit 01d81cdab3
2 changed files with 49 additions and 18 deletions

View File

@@ -947,26 +947,37 @@ forEach({
clone: jqLiteClone,
triggerHandler: function(element, eventName, eventData) {
// Copy event handlers in case event handlers array is modified during execution.
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
eventFnsCopy = shallowCopy(eventFns || []);
triggerHandler: function(element, event, extraParameters) {
eventData = eventData || [];
var dummyEvent, eventFnsCopy, handlerArgs;
var eventName = event.type || event;
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
var event = [{
preventDefault: function() {
this.defaultPrevented = true;
},
isDefaultPrevented: function() {
return this.defaultPrevented === true;
},
stopPropagation: noop
}];
if (eventFns) {
forEach(eventFnsCopy, function(fn) {
fn.apply(element, event.concat(eventData));
});
// Create a dummy event to pass to the handlers
dummyEvent = {
preventDefault: function() { this.defaultPrevented = true; },
isDefaultPrevented: function() { return this.defaultPrevented === true; },
stopPropagation: noop,
type: eventName,
target: element
};
// If a custom event was provided then extend our dummy event with it
if (event.type) {
dummyEvent = extend(dummyEvent, event);
}
// Copy event handlers in case event handlers array is modified during execution.
eventFnsCopy = shallowCopy(eventFns);
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
forEach(eventFnsCopy, function(fn) {
fn.apply(element, handlerArgs);
});
}
}
}, function(fn, name){
/**

View File

@@ -1718,9 +1718,11 @@ describe('jqLite', function() {
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
expect(event.preventDefault).toBeDefined();
expect(event.target).toEqual(element[0]);
expect(event.type).toEqual('click');
});
it('should pass data as an additional argument', function() {
it('should pass extra parameters as an additional argument', function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
data;
@@ -1765,6 +1767,24 @@ describe('jqLite', function() {
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
});
it("should accept a custom event instead of eventName", function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
customEvent = {
type: 'click',
someProp: 'someValue'
},
actualEvent;
element.on('click', pokeSpy);
element.triggerHandler(customEvent);
actualEvent = pokeSpy.mostRecentCall.args[0];
expect(actualEvent.preventDefault).toBeDefined();
expect(actualEvent.someProp).toEqual('someValue');
expect(actualEvent.target).toEqual(element[0]);
expect(actualEvent.type).toEqual('click');
});
});