-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Register multiple event listeners with the same callback in one call #1231
Changes from 7 commits
7eb3886
93fa94f
4ab3378
3817a24
a07f627
9b22207
a10b54a
2f3c2b3
0648c0a
0fd43f2
9fc5f3b
1448638
fc4d00c
191574a
a1dec35
57699db
0954704
b76ea0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,17 +5,35 @@ | |
* robust as jquery's, so there's probably some differences. | ||
*/ | ||
|
||
/** | ||
* Loops through an array of event types and calls the requested method for each type. | ||
* @param {Element|Object} elem Element or object to bind listeners to | ||
* @param {String} type Type of event to bind to. | ||
* @param {Function} fn Event listener. | ||
* @param {String} method Event method (on, off, one). | ||
*/ | ||
vjs.forwardMultipleEvents = function( elem, type, fn, method ) { | ||
vjs.arrayForEach( type, function( type ) { | ||
vjs[ method ](elem, type, fn); //Call the event method for each one of the types | ||
}); | ||
}; | ||
|
||
/** | ||
* Add an event listener to element | ||
* It stores the handler function in a separate cache object | ||
* and adds a generic handler to the element's event, | ||
* along with a unique id (guid) to the element. | ||
* @param {Element|Object} elem Element or object to bind listeners to | ||
* @param {String} type Type of event to bind to. | ||
* @param {String|Array} type Type of event to bind to. | ||
* @param {Function} fn Event listener. | ||
* @private | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we export these we need to remove these |
||
*/ | ||
vjs.on = function(elem, type, fn){ | ||
if (type instanceof Array){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to pull in #1218 soon which adds a |
||
vjs.forwardMultipleEvents(elem, type, fn, 'on'); | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I would just do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, you could just do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright, noted. |
||
} | ||
|
||
var data = vjs.getData(elem); | ||
|
||
// We need a place to store all our handler data | ||
|
@@ -64,7 +82,7 @@ vjs.on = function(elem, type, fn){ | |
/** | ||
* Removes event listeners from an element | ||
* @param {Element|Object} elem Object to remove listeners from | ||
* @param {String=} type Type of listener to remove. Don't include to remove all events from element. | ||
* @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element. | ||
* @param {Function} fn Specific listener to remove. Don't incldue to remove listeners for an event type. | ||
* @private | ||
*/ | ||
|
@@ -77,6 +95,11 @@ vjs.off = function(elem, type, fn) { | |
// If no events exist, nothing to unbind | ||
if (!data.handlers) { return; } | ||
|
||
if (type instanceof Array){ | ||
vjs.forwardMultipleEvents(elem, type, fn, 'off'); | ||
return false; | ||
} | ||
|
||
// Utility function | ||
var removeType = function(t){ | ||
data.handlers[t] = []; | ||
|
@@ -337,11 +360,15 @@ vjs.trigger = function(elem, event) { | |
/** | ||
* Trigger a listener only once for an event | ||
* @param {Element|Object} elem Element or object to | ||
* @param {String} type | ||
* @param {String|Array} type | ||
* @param {Function} fn | ||
* @private | ||
*/ | ||
vjs.one = function(elem, type, fn) { | ||
if (type instanceof Array){ | ||
vjs.forwardMultipleEvents(elem, type, fn, 'one'); | ||
return false; | ||
} | ||
var func = function(){ | ||
vjs.off(elem, type, func); | ||
fn.apply(this, arguments); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,6 +111,10 @@ goog.exportSymbol('videojs.SubtitlesButton', vjs.SubtitlesButton); | |
goog.exportSymbol('videojs.CaptionsButton', vjs.CaptionsButton); | ||
goog.exportSymbol('videojs.ChaptersButton', vjs.ChaptersButton); | ||
|
||
goog.exportSymbol('videojs.on', vjs.on); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have a specific use case where you would like to use these outside of the core codebase? So far we've avoided exposing too many top-level utility functions like this because we're not trying to make a library like jquery or lodash, but the event functions are pretty powerful so I could see exporting these as long as we can point to a reason why we're doing it. If we do export these, we'll need to remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One usecase for exporting it, is to allow videojs plugins the ability to use this so that they wont need to create their own cross-platform/browser solutions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, plugins have added a new issue to the whole utility lib argument. When you're just extending components you have access to most of these functions, e.g. Component.prototype.on(), but general plugins don't get that. It's probably worth opening another issue to discuss these things more deeply.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I think if vjs has these utility methods, it should expose them to plugins. Why force plugin developers to rewrite stuff that's already available? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I exported those functions because I couldn't access them from
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just give There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, thanks! That's how I'm going to do it. |
||
goog.exportSymbol('videojs.off', vjs.off); | ||
goog.exportSymbol('videojs.one', vjs.one); | ||
|
||
goog.exportSymbol('videojs.MediaTechController', vjs.MediaTechController); | ||
goog.exportProperty(vjs.MediaTechController.prototype, 'features', vjs.MediaTechController.prototype.features); | ||
goog.exportProperty(vjs.MediaTechController.prototype.features, 'volumeControl', vjs.MediaTechController.prototype.features.volumeControl); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -832,3 +832,18 @@ vjs.findPosition = function(el) { | |
top: vjs.round(top) | ||
}; | ||
}; | ||
|
||
/* | ||
* Loops through an array and runs a function for each item inside it. | ||
* @param {Array} array The array. | ||
* @param {Function} fn The function to be run for each item. | ||
*/ | ||
vjs.arrayForEach = function(array, fn) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, good idea I will update it soon. |
||
if (!(array instanceof Array) || !(fn instanceof Function)) { | ||
return false; | ||
} | ||
|
||
for (var i = 0, len = array.length; i < len; ++i) { | ||
fn.apply( vjs, [array[i], i, array]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could use fn.call() here an avoid making the extra array. Not sure if there's really any benefit to that though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it would be helpful for other uses of |
||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,32 @@ test('should add and remove an event listener to an element', function(){ | |
vjs.trigger(el, 'click'); // No click should happen. | ||
}); | ||
|
||
test('should add and remove multiple event listeners to an element with a single call', function(){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. great tests! |
||
expect(6); | ||
|
||
var el = document.createElement('div'); | ||
var listener = function(){ | ||
ok(true, 'Callback triggered'); | ||
}; | ||
|
||
vjs.on(el, [ 'click', 'event1', 'event2' ], listener); | ||
|
||
vjs.trigger(el, 'click'); | ||
vjs.trigger(el, 'click'); | ||
vjs.off(el, 'click', listener); | ||
vjs.trigger(el, 'click'); // No click should happen. | ||
|
||
vjs.trigger(el, 'event1'); | ||
vjs.trigger(el, 'event1'); | ||
vjs.off(el, 'event1', listener); | ||
vjs.trigger(el, 'event1'); // No event1 should happen. | ||
|
||
vjs.trigger(el, 'event2'); | ||
vjs.trigger(el, 'event2'); | ||
vjs.off(el, 'event2', listener); | ||
vjs.trigger(el, 'event2'); // No event2 should happen. | ||
}); | ||
|
||
test('should remove all listeners of a type', function(){ | ||
var el = document.createElement('div'); | ||
var clicks = 0; | ||
|
@@ -36,6 +62,30 @@ test('should remove all listeners of a type', function(){ | |
ok(clicks === 2, 'no click listeners fired'); | ||
}); | ||
|
||
test('should remove all listeners of an array of types', function(){ | ||
var el = document.createElement('div'); | ||
var calls = 0; | ||
var listener = function(){ | ||
calls++; | ||
}; | ||
var listener2 = function(){ | ||
calls++; | ||
}; | ||
|
||
vjs.on(el, ['click', 'event1'], listener); | ||
vjs.on(el, ['click', 'event1'], listener2); | ||
vjs.trigger(el, 'click'); // 2 calls | ||
vjs.trigger(el, 'event1'); // 2 calls | ||
|
||
ok(calls === 4, 'both click listeners fired'); | ||
|
||
vjs.off(el, ['click', 'event1']); | ||
vjs.trigger(el, 'click'); // No click should happen. | ||
vjs.trigger(el, 'event1'); // No event1 should happen. | ||
|
||
ok(calls === 4, 'no event listeners fired'); | ||
}); | ||
|
||
test('should remove all listeners from an element', function(){ | ||
expect(2); | ||
|
||
|
@@ -73,6 +123,23 @@ test('should listen only once', function(){ | |
vjs.trigger(el, 'click'); // No click should happen. | ||
}); | ||
|
||
test( 'should listen only once in multiple events from a single call', function(){ | ||
expect(3); | ||
|
||
var el = document.createElement('div'); | ||
var listener = function(){ | ||
ok(true, 'Callback Triggered'); | ||
}; | ||
|
||
vjs.one(el, [ 'click', 'event1', 'event2' ], listener); | ||
vjs.trigger(el, 'click'); // 1 click | ||
vjs.trigger(el, 'click'); // No click should happen. | ||
vjs.trigger(el, 'event1'); // event1 must be handled. | ||
vjs.trigger(el, 'event1'); // No event1 should be handled. | ||
vjs.trigger(el, 'event2'); // event2 must be handled. | ||
vjs.trigger(el, 'event2'); // No event2 should be handled. | ||
}); | ||
|
||
test('should stop immediate propagtion', function(){ | ||
expect(1); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By using the jsDoc comments and adding it to
vjs
it makes this a public function. I don't think it would get used outside of the core codebase so I think we could keep it private and allow closure compiler to mangle it. To make itpublicprivate you could change the name fromvjs.forwardMultipleEvents
to_forwardMultipleEvents
and add the@private
tag to the comment.