You know what the DOM is, and if you don’t it’s easy enough to find out. What isn’t so easily discovered is the answer to this: how should your code communicate with the DOM API? Should it be treated as just another API? Is it no more deserving than, for example, a JSON web service or the web storage API?
What I’ve been seeing a lot of is the DOM becoming incredibly intertwined with the logic, i.e. the program becomes indifferentiable to the DOM. It all ends up being the same:
var counter = document.createElement('div'); counter.innerHTML = '0'; document.body.appendChild(counter); function add() { counter.innerHTML++; } |
Where does the logic stop and the DOM representation of that logic begin?
This is a rudimentary example, and probably something that few of us would be caught doing, but I think similar things are done every day, and not just by beginners. This is normal code. It isn’t some beginner’s hack.
It wears a different mask wherever it rears its head. Here, for example, it’s under the guise of jQuery’s data API:
var alphabetList = { giveHigher: function(dom1, dom2) { return $(dom1).data('word') > $(dom2).data('word') ? dom1 : dom2; }, //... }; |
A shockingly high majority of jQuery.data
uses seem to be making the mistake that I’ve demonstrated.
It’s subtle, not easily spotted. Right now, I’m wondering whether or not it’s acceptable.
For me, it goes against what I’ve picked up in programming thus far. I’m talking about best practices like separating your concerns and maintaining an OO approach, writing clean APIs to access your program’s logic (etc.). To me, the DOM is yet another representation of an object’s data. The data shouldn’t be in the DOM. This is how I would do it:
function Counter() { this.dom = document.createElement('div'); this.n = 0; } Counter.prototype.add = function() { this.dom.innerHTML = ++this.n; }; |
The thing is, the approach here is so ridiculously similar to foo.innerHTML++
that I really can’t tell what I’m making such a fuss over.
The feeling I get when I see foo.innerHTML++
is the same as when I see:
jQuery('div').click(function(){ if ($(this).data('active')) { // do x } else { // do y } $(this).data('active', !$(this).data('active')); }); |
… instead of:
jQuery('div').each(function(){ var active = false; $(this).click(function(){ if (active) { // do x } else { // do y } active = !active; }); }); |
Opinions welcome.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
This sort of mixing is pretty common especially with a lot of jQuery code I’ve seen.
I think the problem lies partially in how jQuery itself is designed: It makes it really simple to work with the DOM. People naturally use the features that are easy to use, and with jQuery, that’s DOM manipulation.
Additionally, jQuery doesn’t provide a facility for creating more general-purpose code. Especially beginners have a harder time creating “classes” with JavaScript itself.
Comparing for example to Dojo, which provides a straightforward way to declare custom classes in a more familiar way and other tools for creating more structured code, you do see code that separates concerns more when working with Dojo. Since Dojo also provides a simple way to declare UI widgets via dijits, you often see general functionality like that defined as classes as well.
Perhaps if jQuery provided a more standard way of declaring widgets and classes, the situation would improve.
DOM, no matter how academic you want it to be, is mentally imaged as the programmatic representation something eventually visual in the browser; you are absolutely right about it has no business in business logic, you shouldn’t even change a word in that statement. It’s horses for courses. I mean, like a store, for about an Apple store, it’s the “back” that says “okay, put 5 MacBook on the window, 10 iphone on the table; and when a customer comes in to buy something, he doesn’t to bring the 5 MacBook from the window, more like it goes to the counter, asks for one, then counter then marks one sold, fetches the one on the window, hands it to the buyer. Instead of keeping the book through the window, like asks the buyer the fetch one himself, then the counter guy walks up to the window and counts how many are left and marks it in the book.
Okay, that’s almost babbling, not exactly the most convincing argument. Unless you’re selling Ferraris where you can counter in one hand how many’s got left in your shop, most should pay attention to their inventory database.
But I think it absolutely is a good practice. I think you’re just a bit of afraid that so many out there are doing it the other way that you’ll take some heat from them. They are doing it the other way, obviously, because most tutorials are like that, and most tutorials are like that because they are teaching what people want to know, which is a quick, quick, quick fix for some problem so their blog or whatever looks a lot more snazzy.
This is, simply put, pointing towards the direction of MVC.
Not a matter of jQuery, on which you just could rely on in terms of manipulating the view, but probably shouldn’t when it comes to the rest.
It is needed to have a piece of abstract code allowing to decouple logic and the DOM API. An example of which being what I’ve mentioned at the beginning.
If there ever was a reason to look down on jQuery, I think this a good one. Or bad, whatever way you want to look at it :p
Good article, the problems stated here are just too common today. There’s an interesting javascript library called knockout ( http://knockoutjs.com/ ) that I’ve been looking at recently. It could mean a way to decouple data from presentation although it might not be the solution for all cases.
Certainly a lot of us have learned that not a good idea to mix business logic and presentation logic. These examples are saying it’s not a good idea to mix DOM logic and presentation logic, which seems like a fine practice to me since the DOM is the presentation. jQuery’s
.data()
API certainly can be used poorly, but take a look at jQuery UI for some examples of how to use it well.In that final example regarding the active flag, the use of
.data()
is much better in that when the div is removed the data will disappear with it, automagically. Imagine each div represents a comment in a large and ever-changing comment stream. What is the lifetime of the closure variable?Imagine the div is instead a textarea, and instead of a custom
active
variable we use the DOM’s built-indisabled
property to manage state and CSS to manage appearance. Is that still bad? Do we really need to create an entire JavaScript object to manage a variable that mirrors the state of the DOM property?If it’s any consolation, I’m wrestling with the same questions – as are many of the developers I work with. A previous commenter said that this was really about moving towards “MVC” – and while I agree with that, it’s possibly more accurate to call it MVVM, in that it seems you are advocating a “DOM proxy object”…or….in MVVM terms, a model who’s sole purpose is to contain data and view behavior for a given view. At the very least, I think devs like us need to be exploring this territory – perhaps one of us will stumble upon something useful. Knockoutjs is becoming popular, and I initially loved it, but that love has cooled significantly the more I’ve thought about how it’s not naturally unobtrusive (and I have some complaints regarding performance and API as well). Other competitors (like Angular and Batman) aren’t unobtrusive either. There *has* to be better ways to do this stuff, and I commend you for asking those questions, and being willing to take the heat from all the jQuery-is-my-spaghetti-code-hammer-and-you-look-like-a-nail fan boys. 🙂
When I was doing iPhone stuff with phoneGap I noticed that having a JavaScript object holding my database greatly improved my performance since I didn’t do unnecessary reads/writes. The database just persisted state across sessions, my real state was held in memory.
Now I’m working on a rather large application for an insurance firm with hundreds of form fields. When we started, the DOM held state. “Is the field visible? Ask the field $(“#blah”).isVisible().” With a very complicated DOM structure and complex business logic, this style of app just wouldn’t perform. Once we treated the DOM like a slow, clunky database and used a fast state control layer in memory to filter “reads and writes” we performed much better. If the field is already visible, don’t invoke jQuery to call visible(). Once we made this view-model layer, we realized we didn’t need sizzle anymore and the app got even faster.
Go with MVVM, it’s the right choice but you won’t really see the difference until the app has to scale.
These examples are saying it’s not a good idea to mix DOM logic and presentation logic, which seems like a fine practice to me since the DOM is the presentation.
Can you do an article/tutorial on “Separation of business logic and presentation logic” since what I think this article touched up on. And the difference between the two. This way more people will be aware. And hopefully I will learn to do it the right way for a change.
I recently wrote some code to filter nodes from a DOM and had issues with NullPointerExceptions:
NodeList nl = doc.getElementsByTagName(“mytag”);
for(int i = 0, n = nl.getLength(); i < n; i++) {
Element elt = (Element) nl.item(i);
if(…) {
elt.getParentNode().removeChild(elt);
}
}
The problem was that NodeLists are live, and removing elements while iterating changes the indexes. When I tried to retrieve an element past the end, nl.item(i) returned null. Even changing the loop to this wouldn’t fix it: for(int i = 0; i < nl.getLength(); i++). The problem then is that if element i is removed, the element that was at index i+1 is now at index i and won’t be checked. I ended up using this code:
NodeList nl = doc.getElementsByTagName("mytag");
for(int i = 0, n = nl.getLength(); i < n; i++) {
Element elt = (Element) nl.item(i);
if(…) {
elt.getParentNode().removeChild(elt);
i–;
n–;
}
}
Please correct me. 🙂
Hi All,
I am having some problems with DOM, I know this is not exactly on the topic going on here but please Can anyone help me?
I let the user create images and buttons (a button with image above it)by clicking on a button: “new_a_window” so I use append() in jQuery. But the question is that I have to get the different image as the user press on it.
As the user click the “new_a_window” button, jQuery will create a button with a image in a jDialog(story_pages).
$( ‘#new_a_window’).bind(‘click’,function(){
$(‘#story_pages’).append(‘
‘);
window_value= window_value+1; // I give each of the images a unique integer value
// since of the value is the index of a image array.
})
I have to implement the scenario:
As the user presses on the image button, I can get the image’s value. However, after appending the code snippet above, each image’s value is “window_value”, I can’t select it by value. How can I do?
Please help me…… 🙁
An article worth reading. This article clearly suggests its not good to incorporate DOM logic into the presentation logic as DOM itself does the job of presentation. An amateur with JS, I look forward for more updates.
Well I am beginner and also an amateur in programming so I won’t comment on the issue directly but in my experience I have seen mixing can help if we know the proportions. May be we need to search the correct proportion here to get a beautiful programme using both business and presentation logic.
Its really simple to work with DOM and i am working with DOM since last several years.
@Jani Hartikainen I accept that jQuery improves the situation but thats also true, to work with jQuery is bit complex than DOM.