I\'ve created a bunch of Backbone.js views. Each view has an associated element (view.el
).
Given an element on the page — out of context of the view — w
I've just written a jQuery plugin for this. It also uses the .data() method.
I have wrapped / proxied the Backbone View setElement method to attach the required data to the view's $el
property.
Registration is done behind the scenes like so:
$(myViewsEl).backboneView(myView);
The plugin traverses up the DOM hierarchy (using .closest()) until it finds an element with the required data entry, i.e a DOM element with an associated view:
var nearestView = $(e.target).backboneView();
In addition, we can specify what type of Backbone View we wish to obtain, continuing up the hierarchy until we find an instance of matching type:
var nearestButtonView = $(e.target).backboneView(ButtonView);
Can be found here.
I hope I am correct in thinking there are no memory leaks involved here; An 'unlink' is performed if setElement
is called a second time round, and since removing a view's element calls .remove() by default, which destroys all data as well. Let me know if you think differently.
(function($) {
// Proxy the original Backbone.View setElement method:
// See: http://backbonejs.org/#View-setElement
var backboneSetElementOriginal = Backbone.View.prototype.setElement;
Backbone.View.prototype.setElement = function(element) {
if (this.el != element) {
$(this.el).backboneView('unlink');
}
$(element).backboneView(this);
return backboneSetElementOriginal.apply(this, arguments);
};
// Create a custom selector to search for the presence of a 'backboneView' data entry:
// This avoids a dependency on a data selector plugin...
$.expr[':'].backboneView = function(element, intStackIndex, arrProperties, arrNodeStack) {
return $(element).data('backboneView') !== undefined;
};
// Plugin internal functions:
var registerViewToElement = function($el, view) {
$el.data('backboneView', view);
};
var getClosestViewFromElement = function($el, viewType) {
var ret = null;
viewType = viewType || Backbone.View;
while ($el.length) {
$el = $el.closest(':backboneView');
ret = $el.length ? $el.data('backboneView') : null;
if (ret instanceof viewType) {
break;
}
else {
$el = $el.parent();
}
}
return ret;
};
// Extra methods:
var methods = {
unlink: function($el) {
$el.removeData('backboneView');
}
};
// Plugin:
$.fn.backboneView = function() {
var ret = this;
var args = Array.prototype.slice.call(arguments, 0);
if ($.isFunction(methods[args[0]])) {
methods[args[0]](this);
}
else if (args[0] && args[0] instanceof Backbone.View) {
registerViewToElement(this.first(), args[0]);
}
else {
ret = getClosestViewFromElement(this.first(), args[0]);
}
return ret;
}
})(jQuery);