Tuesday, May 29, 2012

(ab)using labels in javascript

Something that always felt a bit messy in unobtrusive javascript coding was how we normally use class names for both styling and binding functionality to an element. For example:

<-- HTML -->
<a class="expander" href="javascript:void(0)"></a>

/* CSS */
.expander {/* some arrow styles */}

/* javascript */
$(".expander").click(toggleAccordion)

There are two problems with this:

  1. If I have another type of expander button somewhere else that needs to do something different (e.g. maybe somewhere I need to ajax content in first), then I need to clutter the code with more stuff in order to allows the different buttons to do different things
  2. I have no indication of what said button does by inspecting it w/ Firebug/dev tools, let alone by just looking at it. I have to slog through the code to figure out what code might get attached to this element

Stepping back for a moment

As it turns out, there's a third, minor issue: the href attribute of the element doesn't serve any purpose. It doesn't even tell the user where clicking on the element might go. It's just a clunky way of telling the browser to not do its default action when you click on the element. It's a code smell that can be refactored to something like the this:

$(".expander").click(false)

As it also turns out, browsers as old as IE8 support document.querySelectorAll, so finding elements by href nowadays is not particularly slower than searching by class name. So why not repurpose that useless href back to identifying what a user should expect when clicking on the element?

What's javascript:void(0) anyways?

javascript:void(0) is an antique from a decade ago that most people just blindly use without really thinking about what it might mean. All this line does is run some javascript that returns undefined. Returning undefined in this context is equivalent to e.preventDefault(), which in this case means "prevent going to a URL".

There are many ways of returning undefined, the shortest of which is to simply... do nothing.

<a href="javascript:;"></a>

But we can do better: do nothing in a descriptive way, by using javascript labels:

<a class="expander" href="javascript:expand:;"></a>

And we can target that via jQuery:

$("[href='javascript:expand:;']").click(toggleAccordion)

We can attach different behaviors to different ".expander" elements simply by using a different href:

<a class="expander" href="javascript:expandData:;"></a>

$("[href='javascript:expandData:;']").click(ajaxAccordion)

So now we have a clean way of separating styling from behavior on an element, while making "javascript:void(0)" more informative for users and future maintainer selves.

No comments:

Post a Comment