Depending on jQuery and Perspective
The reported errata for Master Time and Space With JavaScript (buy it here) has been pretty light so far. A bunch of typos, some setup weirdness.
And one interesting issue worth exploring. What is a dependency, and maybe more to the point, where is a dependency?
This issue was raised by a reviewer whose name I’m not going to mention — that’s not a reflection on the reviewer, but rather a reflection on the fact that I’m going to put words in his mouth to expand on his brief comment on the issue, so my interpretation of his position may not actually be his position.
Anyway the reviewer had a comment about when to convert to and from jQuery objects. He raised it in the context of the autocomplete example from the please-go-pay-for-it Book 2, but it also applies to the toggler example in just-for-free Book 1, and really, in any case where you have a JavaScript object which uses a data type that is a jQuery object.
Here’s the deal. I have an object — in this case it’s my autocomplete selector, but I’ll pretend it’s a generic widget because the exact object doesn’t matter — which is being initialized via a function call like so.
widget = new Widget({parentSelector: "#autodiv"});
The key point here being the parentSelector: "#autodiv"
option. In the eventual constructor, that #autodiv
is immediately converted to a jQuery object (The example in the book is more elaborate, I’m simplifying to focus on the issue at hand…)
var Widget = function(options) {
this.$domParent = $(options.parentSelector);
};
The reviewer’s point was that he’d rather convert the selector to jQuery in the call to Widget
and pass the argument to Widget
already converted to a jQuery object, rather than have the Widget
constructor do the conversion:
widget = new Widget({domParent: $("#autodiv")});
var Widget = function(options) {
this.$domParent = options.domParent;
};
I’m not convinced yet that one way is better than the other — I haven’t changed the book code, but I certainly respect this reviewer’s opinion on how to structure JavaScript. But I do think that the two options make different assumptions about the structure of the application that are worth teasing out.
In my structure, jQuery is treated as an implementation detail of the Widget. Since jQuery is an implementation detail, outside code wouldn’t be expected to know about it, so outside code communicates to the Widget via a selector. Now, I grant that there’s a little bit of fiction there, since the selector syntax is (somewhat) jQuery specific. If I really wanted to isolate jQuery, then the calling argument should just be the DOM ID, and the Widget would also be responsible for converting the DOM ID to a jQuery object, with the intermediate step of building a jQuery selector string:
widget = new Widget({parentId: "autodiv"});
var Widget = function(options) {
this.$domParent = $("#" + options.parentId);
};
The advantage of treating jQuery as an implementation detail of my widget is that the rest of my code does not need to know or care about it, and I can presumably even test other parts of my code without jQuery even being around. Also, if I choose to swap jQuery out for some reason, the rest of my code doesn’t need to know, only my widget needs to be changed. I would consider the conceptual point about jQuery being an implementation detail of the widget to be important even if I find it exceptionally unlikely that I would swap out jQuery. (For one thing, it also protects me against jQuery itself changing).
In my reviewer’s structure jQuery is a dependency of the application as a whole. Looked at from that perspective, it makes sense to convert everything to jQuery objects as early as possible, to maintain consistency of representation across the entire application. The code as a whole may be easier to read, since we aren’t continually having to worry about whether an object is a jQuery object or just some kind of DOM identification. If multiple widgets are all using the same jQuery object, then we might prevent some duplicate conversion to jQuery objects. This probably simplifies the internal code at the cost of making us more dependent on jQuery itself. As a practical matter, that tradeoff might be worth it — once we’ve decided to use jQuery, changing it is probably unlikely.
Essentially, it’s a question of where you draw your boundaries. I’m not sure there’s a long-term practical difference between these two structures, in that I don’t think one of them necessarily leads to better or more flexible code over time, especially given even rudimentary attention to practical details. But I do think you should be clear about which structure you are using — mixing the two by treating jQuery as a specific dependency of some parts of the code but a general dependency of other parts would probably lead to confusion later on.
Update:
A reddit commenter asked why I wasn’t passing a DOM element into the Widget, as in:
new Widget({parentSelector: document.getElementBy("autodiv")});
My response: I have nothing really against passing in a DOM element beyond it being a little verbose and an old-timey superstition against using getElementBy. Using a DOM element still keeps the dependency in the widget.
Like this? Go ahead and read Master Time and Space With JavaScript…