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 — what would be the best way to get the view for the element?
For example, say some event affects a bunch of elements on a page and I want to call a method on every view associated with the affected elements.
One way would be to assign the view to the element's data, but I'm wondering if I've missed something smarter:
var myview = BackBone.View.extend({ initialize: function(options) { $(this.el).data('view', this); ... } });
(I'm using Backbone with jQuery 1.5.)
The Backbone. js Views specify how your data looks like. They represent model's data to the users. They can be used with any JavaScript template library.
js Get model is used to get the value of an attribute on a model. Syntax: model. get(attribute)
js setElement method is used to apply backbone view to a different DOM element. It also creates a cached $el reference and displaces the view's delegated events from the old element to the new one.
Backbone. js allows developers to develop one page applications and front-end much easier and better using JavaScript functions. Backbone provides different types of building blocks like models, views, events, routers and collections for assembling client side web applications.
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);
Every view can register for DOM events. As such, every view with the kind of element that you are interested in should register for the DOM event and then assign an event-responding function that does what you want. If you need to DRY things up, use mixin techniques to mix in the function.
I think maybe this solution is easier than you may have initially imagined. Just let the views do the work that they are intended to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With