-
-
Save davemo/954329 to your computer and use it in GitHub Desktop.
// Given a simplified module pattern like so: | |
(function(APP, $, _) { | |
APP.MyWidget = function(opts) { | |
var self = {}; | |
var defaults = { | |
// an array of contexts and declarative event bindings | |
bindings : [ | |
{ | |
// selector of the element events will be delegated to, the "context" | |
'#my-widget' : { | |
// declarative event bindings hash with a pattern of 'event selector' : 'callback' (similar to backbone) | |
'click #view-chart' : 'viewChart', | |
'click #view-table' : 'viewTable', | |
'click #view-related-articles' : 'viewRelatedArticles', | |
'change #metrics' : 'handleMetricChange', | |
'focusout #search-chart' : 'queueResultsTimeout', | |
'click #addremove' : 'toggleAddRemoveEntities' | |
} | |
}, | |
{ | |
// a second context in which to bind events that exists in the same widget | |
'.add-remove-controls' : { | |
'click .close' : 'toggleAddRemoveEntities', | |
'click .result' : 'addEntity', | |
'click .remove' : 'removeEntity' | |
} | |
} | |
] | |
}; | |
// mixin the user defined options to the defaults and assign to self.opts | |
self.opts = $.extend(true, defaults, opts || {}); | |
// the functions that will be called when events happen | |
self.viewChart = function() {}; | |
self.viewTable = function() {}; | |
self.viewRelatedArticles = function() {}; | |
// ... etc | |
// an initializer, immediately invoked | |
self.init = (function() { | |
APP.Util.bindEvents(self, self.opts.bindings); | |
})(); | |
return self; | |
}; | |
})(APP, jQuery, _); | |
// And a util lib that has a bind function | |
(function(APP, $, _) { | |
APP.Util = { | |
bindEvents : function(obj, bindings) { | |
_.each(bindings, function(binding) { | |
var context = _.keys(binding)[0]; | |
_.each(binding[context], function(callback, eventAndSelector) { | |
var parts = eventAndSelector.split(" "); | |
var event = parts[0]; | |
var selector = parts[1]; | |
APP.Util.delegateEventsInContext(context, selector, event, obj[callback]); | |
}); | |
}); | |
}, | |
delegateEventsInContext : function(context, selector, event, callback) { | |
$(context).delegate(selector, event, callback); | |
} | |
}; | |
})(APP, jQuery, _); |
<!-- Leads to binding for a given context automatically, providing an easy interface for event delegation. --> | |
<div id="my-widget"> | |
<a href="#" id="view-chart">View the Chart</a> <!-- I have a click event bound to call the function 'viewChart' --> | |
<a href="#" id="view-table">View the Table</a> <!-- I have a click event bound to call the function 'viewTable' --> | |
<ul class="add-remove-controls"> | |
<li><a href="#" class="result">Canada</a></li> <!-- All these results have a click event bound to call 'handleResultClick' --> | |
<li><a href="#" class="result">Russia</a></li> | |
<li><a href="#" class="result">England</a></li> | |
<li><a href="#" class="result">United States</a></li> | |
<li><a href="#" class="result">Africa</a></li> | |
</ul> | |
</div> |
Sweet! Glad you like it :) I got some positive feedback from a random guy on twitter about it as well. The paradigm worked nice enough in backbone that it seemed easy to yoink.
Updated to support the declaration of multiple contexts for event delegation within a single widget.
Maybe I am missing something. On line 61, I think the params are reversed. Shouldn't it be _.each(binding[context], function(eventAndSelector, callback)?
Nope, in the underscore docs for _.each "If list is a JavaScript object, iterator's arguments will be (value, key, list)." We're iterating over the object that maps to the context, so the params are (value, key) which equates to (callback, eventAndSelector). http://documentcloud.github.com/underscore/#each
Thanks for the explanation! I didn't read the doc. carefully ;) It is a pretty cool code snippet!
This is awesome. Love how brief it is and that I'd be able to incorporate this pattern without adopting an external dependency.