If there’s one problem with frameworks in general it’s that they create dependency. If you start developing your web application with PrototypeJS you have, without noticing, made a long-term decision in regard to how your app will be developed from that day on. This dependency is not easily avoidable. The very concept of framework-centered development relies on it.
When we’re talking about JavaScript specifically this is even more true because many JS frameworks will totally change the way in which you code! jQuery is not only a helper library; it’s a totally new way of programming in JavaScript, and because of this it becomes a nightmare when you want to port over to PrototypeJS or MooTools.
Dependency isn’t always a bad thing but it really needs to be considered before blindly adopting some random framework!
If you want an easily portable web application without this dependency then most people would probably suggest no framework at all, but in my opinion a more modular approach would be better; one which allows you to use any framework but without the dependency:
The idea behind this is to take all easily abstracted functionality and bundle it into a single object, here’s an example:
var URLGatherer = (function(){ /* Easily abstracted functionality: */ var setup = { DOM : { querySelectorAll : jQuery.find }, arrayUtils : { map : jQuery.map, /* jQuery doesn't offer a 'unique' method (for regular arrays) so you could quite easily use one from another library. */ unique : anotherLib.unique } }; return { gather : function() { var anchors = setup.DOM.querySelectorAll('a'), hrefs = setup.arrayUtils.map(anchors, function(anchor){ return anchor.href; }); return setup.arrayUtils.unique(hrefs); } }; })(); /* Usage: */ URLGatherer.gather(); // Returns array containing all HREFs on page |
All “easily abstracted functionality” has been put in the setup
object. The benefit of using this model is that no real dependencies are created. Switching from jQuery to Dojo or from Prototype to MooTools is no problem because you’re containing their abstractions in a single object which can be easily changed at any time!
Here’s a more involved example:
var myKillerApp = (function(){ var setup = { DOM : { querySelectorAll : jQuery.find, animate : function( elem, props, dur, fn ) { return jQuery.fn.animate.call( jQuery(elem), props, dur, fn ); }, append : function(elem, toAppend){ return jQuery.fn.append.call(jQuery(elem), toAppend); }, prepend : function(elem, toPrepend){ return jQuery.fn.prepend.call(jQuery(elem), toPrepend); }, attr : function( elem, props ) { return jQuery.fn.attr.call( jQuery(elem), props ); }, ready : jQuery(document).ready }, load : { json : jQuery.getJSON, xml : jQuery.get } }; return { init : function() { var app = this; setup.documentReady (function(){ app.load('data.json', function(data){ app.inject(data.latestNews); }); }); }, load : function(location, callback) { var format = location.match(/.([a-z0-9]+)/i)[1]; setup.load[format](location, callback); }, inject : function(what) { var appContainer = setup.DOM.querySelectorAll('#appContainer'); setup.DOM.append(appContainer, what); } }; })(); /* Calling the app: */ myKillerApp.init(); |
Again, we’re containing everything within the ‘setup’ object, entirely accessible within the scope of our application!
While the naming conventions used above are quite long-winded, that is certainly not a limitation of this model. You can use whatever naming convention you want!
Thoughts…
Consigning to an entire framework may be suitable for some projects but for others a more versatile model, like that shown above, may be better. This separation of the framework’s abstractions from the desired functionality probably won’t work very well with class-based libraries because the coding conventions they employ are not easily avoidable. For example, it would be hard to employ a system like this for MooTools because of its class-based paradigm. But jQuery, being a very functional implementation, allows this kind of model.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
An example of this is the ExtJS framework. They use a base object for all core functionality, and then the Ext stuff sits on top of that. The base object can be jQuery, Prototype, YUI, or Ext (they have a base object as well). I think there might be a MooTools and a Dojo base class as well.
Swiss – a JavaScript framework framework – developed and used by Appcelerator does this abstraction for you:
http://github.com/jhaynie/swiss/tree/master
Pretty interesting article but I have a couple of questions regarding the second example:
1. Is setup.documentReady supposed to be setup.DOM.ready or is it referring to something else?
2. How does setup.load[format](location, callback) work? From what I understand setup.load has no arguments so are they inherited from jQuery.get or jQuery.getJSON depending on the filetype or something else?
Great article and great blog. Keep up the good work!