Ah, there was a time early on during the days of classic ASP when I absolutely despised JavaScript. I'm not sure if it was because my untrained eyes invariably missed the periods, or my clumsy fingers were not used to the case-sensitivity, or...I just didn't quite "get it". Actually, it was all of those things.
More time passed, I became slightly less naive when it came to programming. More importantly, all the AJAX hype came to the forefront. I found myself using JavaScript for more than just elementary client-side validation functions. I suddenly realized I had to actually learn the language that I fostered such hatred for. LEARN?!
Fast forward a few years...I can't really believe I hated JavaScript as much as I did. JavaScript is a language that is sort of like single-malt scotch. Totally disgusting if you aren't sure what you want. Exquisite in every way if you do. I do a good amount of work in JavaScript these days...both using pure JS and jQuery-magic when appropriate. More often than not I'm writing some AJAX functionality to manipulate the DOM based on some response I get from a service/DB/dolphin/etc.
I've come to realize that it's easy to write a function, especially in JS, that is cumbersome and not-at-all conducive to being re-used. A while back, I wrote a function that looked like this:
So, what is wrong with this picture? It could be worse, right? I'm passing in the ID of the DOM element that contains, in theory, the e-mail that should get subscribed to updates. I run that through my ValidateEmail() function. If the e-mail isn't valid, I go ahead and hide the success DIV, and show the error DIV..and vice versa. What? It's clever, isn't it?
Sure. It's great...until a month later you need to use my function and come to the startling realization that the way I wrote my function forces you to have specific elements in your DOM. A damn shame, really. Let's count how many things I see wrong with this method:
1. It forces the caller to pass in an the IDs of DOM elements. What happens if I want to pass by class? The hard-coded "#" screws everything up. This applies to all the parameters. If I was going to do this at all, I should have passed selectors.
2. The function leaves me no choice but to have a DOM element for a valid e-mail address, as well as a DOM element for an invalid e-mail address. It's also not at all obvious what those DOM elements accomplish. I happen to know the DOM elements contain validation success/error messages, but someone else wouldn't immediately know that.
3. Even if I have DOM elements for "success" and "error", I'm modifying the display attribute of my CSS. What if I'd like to toggle a class instead using addClass()?
In summary, then, I am stupid. But more specifically, I was entirely too short-sighted when I created this function. Sure, it's just one lousy JS function. It serves its purpose. The issue is, it really only serves its purpose given very specific constraints. This is far less than ideal.
Let's take another crack at this function and see what'd it look like if we incorporate the corrections I allude to above.
Okay. That's a little better.
Now I can pass selectors instead of be bound to passing only DOM IDs. I have also used the jQuery CSS(cssObj) method. This let's you pass a JSON object that is essentially a list of your CSS Attributes and their values. For example, the value of the successCssObj might be this:
At this point, you're probably wondering why I ignored problem number 2, above. The caller is still bound to having a "success" and an "error" DOM element. I could get around this downright silly requirement in a few ways. I could easily check if I've passed a selector in for the "successSelector" and the "errorSelector", and proceed to modify their CSS if they exist. But why bother? That's ugly, it's more code, and it's just going to confuse the crap out of me when I come back to this in another month.
There is an easier way
And that way is by using callbacks. If you've used jQuery, or any of the other popular JS frameworks floating around, you've been using them already without even thinking about it. When you call $ajax in jQuery, you're providing both success and error callbacks. Basically, you're optionally passing in a function you'd like to execute if an error occurs, and a function you'd like to execute if no error occurs. The key here is you're passing in the function to execute, and the function contains the logic you to run given a certain condition (An invalid e-mail, in this case). This means that my function is only responsible for doing what it is named....Subscribing an email address for updates. It doesn't care about the caller's DOM, and it sure as hell doesn't care about CSS. My function doesn't even like CSS. Let's take a look at this glorious function after a makeover.
There we go, and we've even made the successCallback and errorCallback parameters optional if we'd prefer not to pass either or both of them. As promised, we've totally separated the caller's context from the objective of the function. Let's take a look at an example of calling this function. Again, this will look very familiar to you if you're used jQuery. That's a good thing.
There's a reason jQuery and other libraries are created in this manner. It makes it much easier to reuse your code. The combination of using callbacks and optional parameters really inspires a shift in the way I analyze requirements and code methods in general...especially in JavaScript. Furthermore, it doesn't increase complexity. If anything, it is easier to read methods when they truly have one purpose. You don't want to force any method caller/client to have additional requirements, be it DOM elements or anything else.