EnterpriseDojo.com uses jQuery!

And you should too ... for your WordPress blog that came with jQuery already installed and probably barely even needs JavaScript in the first place. 

Here's an idea: Let's have an honest discussion about when a given solution makes sense and when it doesn't, rather than mocking a contributor to that discussion for choosing a perfectly appropriate tool for a perfectly mundane task. EnterpriseDojo.com is a blog about using Dojo in the enterprise, not a blog about using Dojo on blogs. Snickering at its use of jQuery is approximately as useful as pointing to this graph and saying case closed, leaving more reasonable, thoughtful people to explain all the ways that graph doesn't tell us a damn thing

I want to be very clear, as the dust settles around my several recent rants: jQuery has its place, and it is a very, very big place. It is a lovely DOM, Ajax, and events library, and a great way to get certain things done quickly, especially for people who may not have the luxury of learning the inner workings of JavaScript (noble a goal as that is). There was a time when DOM and Ajax and events questions felt like the questions of the day, and jQuery showed us how to answer those questions using a simple, easy-to-learn API that suddenly made JavaScript accessible to the masses. Other libraries solved the same questions but those solutions felt ugly and clunky and goshdarnit, hard, and I give jQuery the utmost credit for making JavaScript suck so much less when all I wanted to do was show or hide or slide a thing or load some content onto my page. I'll even grant that it can be used as a piece of a large application solution, though I question the wisdom of doing so for reasons outlined in another post. Heck, I'll even grant that "enterprises" aren't off their rockers to use it -- for the things it was meant to be used for. 

What upsets me is when smart people seem to say that jQuery's victory in an internet-wide popularity contest suggests, well, anything at all when it comes to more complex needs. It upsets me because my clients hear those suggestions, look at that same well-marketed graph, and I am left explaining to them that, yes, jQuery is popular, but it's popular because it answers a small set of questions easily and well -- even for people who don't even know JavaScript! -- and you, dear client, have vastly larger questions than that.

At the end of the day, these toolkit decisions ought to be about more than a popularity contest; jQuery may be the right answer, or part of the right answer, but it's imperative, to me, that my clients understand the scope of the question first. It's imperative that they make their decisions based on a full understanding of pros and cons, risk and reward, cost and benefit -- not based on a graph, not based on a popularity contest.

So again I say: we, as a community, and especially the influential ones among us, do well when we elevate the conversation beyond that contest and acknowledge that choosing the right toolkit depends on first understanding what you're choosing the toolkit for. We do well when we educate teams and decisionmakers on the lessons we've learned in the time since DOM, Ajax, and events were the big question of the day, on the best practices that have emerged, on the situations where we've, gasp, had to look beyond jQuery -- either to other tools or other toolkits -- for the answers. And we do well when we start showing them how they can do the same.

An aside: Some people I like a lot have pointed out that in the midst of all of my complaining, I have not come out and offered a solution. This is fair. Two things: One, I have not wanted this conversation to collapse into Dojo vs. jQuery vs. YUI vs. Ext vs. MooTools vs. whatever, because if it does, I think we've missed the crux of the matter: that different tools do different things, that some set out to answer complex questions and some do not. If anyone has been unclear, which I rather doubt, my personal preference of late has been to use Dojo. I do not recommend using Dojo for everything under the sun, but I find it offers a lot of utility when writing non-trivial applications. I cannot make a compelling argument for using it vs. YUI, simply because I don't know YUI well enough. I can't even make a compelling argument for using it vs. Ext, except for licensing issues that may or may not be relevant in a given scenario. Two, if you're disappointed that I haven't offered a solution, especially a jQuery-based one, I apologize. However, I feel there are too many viable existing solutions out there already, and I haven't come up with a good reason to promote a jQuery-based solution besides jQuery's popularity. And, well, see above for my thoughts on that.

Loading mentions Retweet

Filed under  //  dojo   jquery   large-applications  
Comments (4)
Posted 12 days ago

On Rolling Your Own

There’s been a lot of activity around my last post, On jQuery & Large Applications. A number of people have asked me why, exactly, I’m so opposed to using jQuery as part of a roll-your-own solution.

To answer that, let’s start by looking at (some of) the pieces my ideal large application solution might include:

  • DOM manipulation tools
  • Ajax tools
  • A dependency management and build system
  • Clear patterns for code organization, such as namespaced modules
  • An inheritance system, preferably one that offers multiple inheritance, for sharing code across modules and staying DRY
  • A non-DOM-centric, loosely coupled API for communication between modules
  • A widget system that makes use of the inheritance system, with lifecycle management (setup/teardown) and templating
  • A system for maintaining templates separate from JavaScript while interning them into the build to eliminate HTTP requests
  • A system for abstracting RESTful server communication
  • For a UI-intensive project, a rich widget system pluggable to arbitrary data sources and designed with an easily-extended API
  • For an enterprise project, a11y and i18n provisions, as well as clear patterns for file organization

To all of you who have said you can do this yourself, you win. I can’t argue with you without sounding like I’m saying you’re too dumb, and you’re probably not.

But here’s my question: why? What, exactly — exactly — do you gain by putting all of this together for yourself, rather than using the pieces you need of a toolkit that provides all of this out of the box? Because here are a few things I think you lose:

  • Integration. Is your abstracted RESTful data API designed to talk to your widget system that’s designed to talk to your template system? That’s hott. Now what about when a new version of one of those components comes out that violates an assumption you made?
  • Maintenance. I heart the good folks who have put together individual answers to these individual questions, but they have no obligation to continue being the good folks they are, and they certainly have no obligation to do it on any sort of schedule. Remember all those plugins that broke with jQuery 1.4? That sure was fun.
  • Documentation. I’m going to grant you, right now, that jQuery is one of the best-documented JavaScript libraries out there, hands down. But what about all these other pieces you’re putting together? Especially the ones you really are rolling on your own, like that templated widget thing that communicates so nicely with your abstract data API. There are a wealth of resources for understanding, troubleshooting, and using these pieces in established toolkits. Where will the next developer turn when they have questions about yours?
  • Experience. Like I said, I get that you’re smart. Possibly smarter than me. That’s cool. But are you smarter than the combined wisdom of a team of people that has been thinking about these questions for years? Are you sure your solution has thought through all the questions they have?

I’ve noticed that, in the conversations I’ve had the last few days, it seems to fall to me to “prove” that a roll-your-own solution that includes jQuery isn’t advisable. Perhaps that’s fair — “you started it!”, you might say, and that I did. But simultaneously, others argue that jQuery never set out to answer these questions, and so it’s not jQuery’s fault that people are trying to use it in ways it wasn’t intended to be used. I have waited in vain to hear a compelling reason why jQuery should be part of a large application solution, to hear why I should recommend a roll-your-own solution that includes jQuery to my clients. The extent of the argument seems to be “because I like it, and it doesn’t force me to think a certain way.”

No one puts baby in a corner. Got it. But the straw man-ness of this argument has me, literally, chuckling right now. Let’s not confuse a mythical one-size-fits-all solution with a toolkit that provides, well, tools. Tools to do all sorts of things, tools meant to work together, tools developed and tested and maintained by a whole big team of smart people, tools that are, actively, being used in really frigging big, really frigging enterprisey applications.

I very purposefully didn’t propose a particular alternate solution in the original post, but it’s hardly a secret that my personal favorite, of late, has been Dojo. Not because it purports to solve every problem or prescribes how to solve them, but because it gives me so many tools to use to solve a given problem. Time and again I find that “Dojo already did that” — they already wrote the tool I’m wishing I had. Now I don’t have to write it, and, perhaps more importantly, I know it was written to work with all of the pieces I’m already using, and when I use it I’m not risking duplication of code or a lack of testing, maintenance or support. Win.

But let’s be very clear: no one’s forcing me to use that component! No one is forcing me to do things a certain way, any more than jQuery is “forcing” me to think of my application entirely in terms of the DOM. I can write my own component if I want, or use someone else’s if I want, or change it a bit if I want! For example, on a current project I pulled in mustache.js because the project had a lot of templates that had already been written to use it. The brilliant thing, though, was that integrating mustache.js into dijit._Templated instead of the standard templating system was trivial. That component, and all the others in Dojo and Dijit, are architected explicitly not to be one-size-fits-all. They provide a rock-solid base for large application development, for getting up and running quickly using a bevy of ready-made solutions, but also provide so many extension points that you can turn those solutions on their head if you want or need.

Garann Means, whose blog you should be reading, took a bit of issue with my original post in model-view-controller and comfy clothes.

I do agree that it benefits everyone to be working in the same setup and making use of tools that have been vetted by geniuses whose entire job is to create such things. But I’m dubious about any approach which comes too close to promising one size fits all. If you’ve ever sewn a dress, you understand that one size fits all is technically possible, but some people are going be left with a lot of excess while others will scarcely be able to breathe.

Carrying on that metaphor, these pieces provided by Dojo — or any other comprehensive toolkit, for that matter — are but starter patterns, and thread, and scissors, and pins, and a sewing machine, and OK I’m stretching the metaphor, now, but my point is they’re definitely not finished one-size-fits-nobody garments. On the other hand, if I decide to use jQuery in a large application, it can feel like I’ve been given a black marker and some of that crinkly brown paper, and now it’s up to me to draw a pattern and then come up with all those other pieces, too. Intellectually interesting and pleasingly crafty, perhaps, but not particularly efficient, sustainable, repeatable, or maintainable.

So again I ask, in all seriousness and in hopes of fostering a good discussion: Why? jQuery provides you with DOM, Ajax, and event management tools, but little else. There are tools designed for building large applications, designed to provide all of the pieces I want and so many more it’s not even funny, and they provide you with DOM, Ajax, and event management tools, too. What’s the compelling case for rolling your own solution that includes jQuery instead?

Loading mentions Retweet

Filed under  //  dojo   jquery   large-applications  
Comments (17)
Posted 22 days ago

On jQuery & Large Applications

Update: I’ve written a separate post on the wisdom of rolling your own large application toolkit that incorporates jQuery.

I’ve been thinking a lot lately about JavaScript applications. As my skills have evolved, I’ve had the privilege of working on more actual applications, and I’ve gotten further and further from clients who want to add a bit of Ajax or bling to an otherwise fairly traditional web site.

The most interesting applications I work on are client-side intensive: the server is responsible for providing data as JSON to the client, and most everything else — templating, state management, data management, site navigation, and of course user interaction — is left to the client side.

It’s a lovely way of writing an application. There’s no need for me to touch server-side code; in some cases I work with a server-side developer to decide what the data they send will look like, but in others I just take what an API already provides and make it work. I get to use the same templating framework across projects, regardless of server-side technology, and I can prototype complex interactions before the server side even exists.

This is a land where HTML, CSS, and JavaScript are almost all you need, and I like it. I’ve become a firm believer in moving giant hunks of functionality that used to belong to the server over to the client. For a variety of reasons, I think it’s clear that this is where most interesting web development is headed, to the extent it’s not already there.


This style of building an application changes the front-end development game. In fact, “development” may no longer be an adequate description; we’re moving into the realm of engineering, here. We’re not using JavaScript to add a bit of bling to our sites — a slideshow here, some Ajax there — we’re architecting an application, damnit. We can’t just write some procedural code that binds a bunch of anonymous functions to some events and call it a day; if we do, I can tell you from experience that we’re going to end up with a steaming pile of unmaintainable crap.

Among a host of questions presented by these sorts of applications, some of the most interesting to me are:

  • What are the units of functionality that will make up the application?
  • How will those pieces be organized into units of code?
  • How will those pieces communicate with each other?
  • How will dependencies between components be expressed and managed while adhering to the principle of loose coupling?
  • How will components manifest themselves in the DOM? Do they need to?
  • How will we persist data across URL and page loads?
  • How will we manage communication with the server?
  • How will we make sure users only see the data they’re allowed to see?

At the risk of making a broad generalization, this isn’t the way today’s average JavaScripter learned to think. The mantra of jQuery, the most popular JavaScript library on the internets, is “get some elements, do something with them” — perfectly terrible preparation for analyzing an application from a perspective other than the DOM. And, IMHO, therein lies a tremendous problem.


As more and more application logic moves to the browser, I’m eager to see the JavaScript community rise to the challenge, but instead it feels like the opposite is happening. People with little understanding or appreciation of these questions are taking on projects that demand these questions be answered. The result is a land of fragile code that gets the job done while giving the finger to the next developer; a land of code so tightly coupled, so deeply beholden to the DOM, so blatantly not reusable or extensible or maintainable as to render every subsequent commit a complete crapshoot, as liable to cripple the application as not. The viability of the project is threatened, and so is the reputation of JavaScript.

We are better than this. JavaScript, even that old-fashioned browser kind, is a language worthy of respect, not a thing to be dreaded. But — and here’s the sentence I have struggled 10 months to realize and an hour to write: in order to prove that we are better than this, we must make abundantly clear to the budding developers, to the project managers, to the enterprises, to anyone intending to build a remotely complex JavaScript application, that there’s more to JavaScript than jQuery. The questions are bigger, the answers more complex, and the relevant skills, alas, a bit harder to come by.

We have to make clear that, in fact, jQuery is but a hammer. When it comes to building these intensively client-side applications, we’re talking about building skyscrapers, for god’s sake. The problems solved by a hammer are the least of our concerns.


It was just a few months ago that I gave a presentation on building large jQuery applications. I emphasized jQuery’s role as strictly a DOM and Ajax tool, and demonstrated a few other tools — John Resig’s simple inheritance, James Burke’s RequireJS dependency management and build tool, Jan Lenhardt’s mustache.js — that one would want to bring to the table for such an undertaking.

But to what end do we assemble said hodgepodge of tools? Is it just so we can continue to “use jQuery”?

jQuery’s API is, indeed, dead-simple, but we are smart people! We are building skyscrapers! When it’s time to write a complex application, and we need all of these things that jQuery doesn’t offer, can we not learn to use another hammer — learn that dojo.place('<div>I am new!</div>', oldDomElement, 'last') means the same thing as $('<div>I am new!</div>').appendTo(oldDomElement) — if learning it gives us access to legions more functionality than jQuery even aspires to provide?

Do we assemble this hodgepodge because finding jQuery developers is perceived as an easier task than finding practitioners of another library, even though someone saying they “know jQuery” is little indication that they will know how to work with the assembled solution?

Do we do it for the plugin ecosystem — full of code of varying quality and maintenance — even though many of the large application needs addressed by those plugins are addressed by other libraries as well, and sometimes better?

And when we do it, when we assemble this collection of tools ourselves, what risks are we accepting? What price will we pay down the road to maintain three or five or 10 different pieces from three or five or 10 different authors, with different release cycles, no guarantee of compatibility or maintenance, and no central project thoughtfully considering their future?


I’ve wrestled with these questions for months, agonizing during sleepless early-morning hours over how to advise clients on the answers. I’m the co-host of yayQuery, a contributor to the jQuery Cookbook, and, I’ll venture to say, a decently respected member of the jQuery community. I did not arrive at this conclusion lightly, and I have few illusions it will be well-received, or even heeded.

But I’ve grown weary of people championing a tool that simply does not answer the big questions I see in project after intensively client-side project. I’ve grown weary of those same people dismissing tools that answer those questions handily and have been answering them for a while now. I cringe when clients tell me they’ve chosen jQuery because it was “easy,” and then watch them predictably struggle with all of the questions it does not answer. And I’ve found I can’t continue to bite my tongue when people recommend jQuery as an enterprise-grade solution while failing to acknowledge these questions, let alone answer them*.


I do not want to see jQuery go away. The simplicity of its API was undeniably instrumental in the rise of JavaScript as a language these last few years. It is a perfect gateway drug, and I greatly enjoy watching people transition from “get some elements, do something with them” to the elegant patterns of JavaScript itself.

jQuery is an entirely appropriate answer to so many questions, but it falls so short for large applications, forcing you to assemble such a tenuous toolkit of your own, that it simply isn’t a viable answer — or, in my opinion, part of an answer — for large applications. If we hope to continue to gain respect as a community, we ought to admire jQuery’s immense contributions, but we must not be afraid to accept and make very clear its limitations. We do otherwise at our peril.


*An aside: To its credit, JupiterIT has put forward JavaScriptMVC, the only substantive attempt I’ve seen at answering these large application questions using jQuery. I applaud them, but fear their efforts will continue to be somewhat isolated without the support and endorsement of the wider jQuery community. If you have read this far and still have your heart set on a jQuery-centric large application solution, you should by all means take a look at JavaScriptMVC.

Loading mentions Retweet

Filed under  //  jquery   large-applications  
Comments (54)
Posted 24 days ago

Announcing jQuery Fundamentals: An Open-Source jQuery Training Curriculum

I've been leading jQuery trainings for more than a year now, from tiny gatherings that I organized myself at the local coworking space, to intensive two-day sessions at local web companies, to whirlwind one-day classes at governmental agencies. Over the course of those trainings, I've developed what I'd like to think is a decent curriculum -- training material that's the size of a small book, exercises that demonstrate core concepts, and solutions to those exercises that students can peek at later or when they get stuck. I decided recently that it was time for all of this material to see the light of day, so I spent the last several days converting it all to DocBook files that allow for easy publication to HTML and PDF (and other formats, if I'm later so inclined). I also fleshed out some topics that I'd given short shrift, and started planning sections covering advanced topics such as plugin authoring, code organization, best practices, and more. There's more to come in the next few days, but I think what I've done so far is worth sharing. I hope you'll agree.



My goals in releasing this are several. First and foremost, I want to see people writing better jQuery. The free resources for learning jQuery are scattered across the internets, and my personal experience of learning the library was haphazard — it was a long time before I learned some things I wish I'd known from the get-go. In addition, I want people who are writing jQuery to understand JavaScript. To that end, the book begins with a survey of JavaScript itself before jumping into jQuery. Finally, I want to enlist the bright minds of the jQuery community to help developing a robust, authoritative, in-depth jQuery curriculum, and in exchange it only seemed fair to make it available to everyone. I should mention that the goal of this material is to serve as a companion to a human instructor. That said, individuals may find it useful for self-study, especially if they're diligent about doing the exercises at the end of each chapter. If you're inclined to help -- by adding a chapter, a section, a paragraph, an exercise, or even just a correction -- fork the repo and send me a pull request. I look forward to seeing how this project might evolve with the community's help. Note: If you comment on this post pointing out an issue with the material, I will do my best to tend to the issue, but I probably won't publish your comment, as this post isn't the right place for reporting issues in the code. You can report issues at the repository, but if it's important to you, please fork the repository, make the change, and send me a pull request.

Loading mentions Retweet

Filed under  //  front-end development   howto   javascript   jquery   jquery fundamentals   training  
Comments (35)
Posted 2 months ago

jQuery: Storing and retrieving data related to elements

It’s very common to need to get information about a DOM element when a user interacts with it — for example, perhaps you have an unordered list of names, and when a user clicks on a name, you want to show a picture of the person above the list. In order to do this, you need to figure out which person the clicked list item represents. Many beginning jQuery users will attempt to achieve this by putting ID attributes on each list item, such as id="rebecca". Then, they’ll read the ID attribute off the clicked element and use it to build a URL for the related image.

<ul class="people">
<li id="paul">Paul</li>
<li id="rebecca">Rebecca</li>
<li id="alex">Alex</li>
<li id="adam">Adam</li>
</ul>


var portrait = $('#portrait');

$('ul.people li').click(function() { 
    var name = $(this).attr('id');
    portrait.html('<img class="posterous_download_image" src="/images/people/'%20+%20name%20+%20'.jpg" alt="" />');
});

Strictly speaking, this will work. But is the ID really the right place to store this information? What if you need this behavior on other elements on the page too? You can’t have more than one element with the same ID on the page, so you might find yourself using funny prefixes in your IDs, like person_rebecca, and then stripping out the prefix. You could do it with classes, but then you’d have the opposite problem: you’re using (generally) unique classes like rebecca, but really classes are meant to indicate similarities among a set of elements. And what if you need to store more than one piece of information about an element on the element? Next thing you know you’ve got id="person_alex_red" and you’re jumping through all sorts of hoops to parse out the data you need.

Custom Data Attributes

HTML5 makes available [custom data attributes](http://dev.w3.org/html5/spec/Overview.html#custom-data-attribute, and they prove to be a much more elegant and robust solution to this problem. They’re custom, so they can contain pretty much anything you want, and each element can have as many of them as you want.

<ul class="people">
  <li>Paul</li>
  <li>Rebecca</li>
  <li>Alex</li>
  <li>Adam</li>
</ul>


var $portrait = $('#portrait');

$('ul.people li').click(function() { 
    var $li = $(this),
        name = $li.attr('data-name'),       
        color = $li.attr('data-hairColor'),
        $img = $('<img class="posterous_download_image" src="/images/people/'%20+%20name%20+%20'.jpg" alt="" />');

    $portrait.append($img).css('border', '5px solid ' + color);
});

$.fn.data

When you want to embed information about an element in the HTML you send down from your server, custom data attributes offer a clear and easy solution. But what if you want to attach data to elements that you’ve added to the page using JavaScript? For example, you might have some data you fetched from your server via an Ajax request:

{
    "items" : [
        { "name" : "Paul", "image" : "paul", "hairColor" : "black" },
        { "name" : "Rebecca", "image" : "rebecca", "hairColor" : "brown" },
        { "name" : "Alex", "image" : "alex", "hairColor" : "red" },
        { "name" : "Adam", "image" : "adam", "hairColor" : "red" }
    ]
}

You’re going to iterate over the data to produce a structure much like the one above, but in this case, it doesn’t make sense to store the related data in markup, because you’ll just have to extract it again later. Instead, you can use the $.fn.data() method in jQuery to store the data using JavaScript instead of markup:

var $target = $('ul.people');

$.each(response.items, function(i, data) {
    $('<li/>', { html : data.name })        
      .data({  
        name : data.image,              
        hairColor: data.hairColor       
      })        
      .appendTo($target); 
});

Later, you can read the data off the element using the $.fn.data() method again, this time passing just the name of the key you’re after:

var $portrait = $('#portrait');

$('ul.people li').click(function() { 
    var $li = $(this),
        name = $li.data('name'),
        color = $li.data('hairColor'),
        $img = $('<img class="posterous_download_image" src="/images/people/'%20+%20name%20+%20'.jpg" alt="" />');

    $portrait.append($img).css('border', '5px solid ' + color);
});

Mixing the two methods

This is all well and good, but what if you have a list of people that was sent from the server using HTML and custom data attributes, and then you add elements to it later using JavaScript and store data on them using $.fn.data()? Now your data is stored on elements in two different ways, so how do you extract it reliably? One option is to handle both cases. First, you’ll switch to using jQuery’s delegate method for the event binding, so you don’t have to keep binding click handlers as you add list items to your list. Then, inside of your click handler, you’ll figure out where you can get your data from:

var $portrait = $('#portrait');

$('ul.people').delegate('li', 'click', function() { 
    var $li = $(this), 
        name = $li.attr('data-name'), 
        color, $img;

    if (!name) { // did the li have custom data attributes?
        name = $li.data('name');
        color = $li.data('hairColor');
    } else {
        color = $li.attr('data-hairColor');
    }

    $img = $('<img class="posterous_download_image" src="/images/people/'%20+%20name%20+%20'.jpg" alt="" />');
    $portrait.append($img).css('border', '5px solid ' + color);
});

Another option is to iterate over the original elements, and store the data from the custom data attributes using the $.fn.data() method:

var $portrait = $('#portrait'), 
      $ul = $('ul.people');

$ul.find('li').each(function() {
    var $li = $(this);
    $li.data({
        name : $li.attr('data-name'),
        hairColor : $li.attr('data-hairColor')
    });
});

$('ul.people').delegate('li', 'click', function() { 
    var $li = $(this), 
        name = $li.data('name'),
        color = $li.data('hairColor'),
        $img = $('<img class="posterous_download_image" src="/images/people/'%20+%20name%20+%20'.jpg" alt="" />');

    $portrait.append($img).css('border', '5px solid ' + color);
});

// load more list items via ajax at some point
// to make that whole delegate thing worthwhile

Which option you use will depend on how large your original list is (and thus how long it will take to iterate over it), and how likely people are to click on a lot of items in the list (and thus whether the initial iteration is worth the time). I leave it as an exercise for the reader to decide which approach makes sense for you.

In Conclusion

When you need to attach data to elements and then extract that data later, there are options beyond classes and IDs, and in fact classes and IDs may be an especially poor way to approach the problem. Taking advantage of custom data attributes and the $.fn.data() method in jQuery can make it painless to store and retrieve data related to elements, and the metadata plugin can streamline the process for you even further.

Further Reading

Loading mentions Retweet

Filed under  //  data   howto   html5   javascript   jquery  
Comments (8)
Posted 2 months ago

srchr: Crowdsourcing JavaScript wisdom

UPDATE: The deadline for completing your submission is April 16, the day before JSConf. If you're at the conference, join others in the hacker lounge to see what they did! I've been working on a blog post about using classes and pub/sub for structuring jQuery applications, and I had in mind a pretty simple demo app that I was going to build. I also wanted to show a version of the app that was built in a more traditional way, and I'd been pondering whether I should write that version myself, or see if I could cajole someone else into doing it. And then, a moment of inspiration: rather than a contrived counter-example, why not get a whole bunch of developers to show how they'd tackle the problem, so we can all gain from the exercise and learn from each other? I tweeted my idea, and five minutes later I had a dozen volunteers and counting, which is downright awesome and in hindsight shouldn't be surprising. It's so rare that we get to see multiple approaches to a moderately complex problem -- it's much more common to see horrendous code and bitch about it :)

The project

I've put together a mock/spec for a small, strictly client-side application that uses YQL to search for content and then displays it to the user. (Click on the image to see it full-size.)
Think of this as an exercise in creating a product, not a site that you finish and walk away from -- the goal is to create an extensible, modular application. That said, there are no "right" answers here: the point is for you to demonstrate how you, personally, would approach the problem.

Presenting your solution

I've created a github repository for the project that contains nothing more than some documentation, the mock/spec, and a few stub files and directories. You should fork this repository to get started. If you create some CSS that you'd like to share, I'd encourage you to send a pull request so I can make it available to everyone; this isn't an CSS exercise, so no one should labor over that part if they don't want to. I may very well write some basic CSS myself in the next couple of days, but it's late :) Finally: please comment on this post if you have any questions!

Loading mentions Retweet

Filed under  //  front-end development   javascript   jquery  
Comments (17)
Posted 5 months ago

Building a standalone JavaScriptMVC controller.js

I've been doing a lot of work lately with a client who's using just the controller portion of JavaScriptMVC. I plan to write a more in-depth post about this while I'm traveling this week, but in the meantime, I wanted to jot down the steps to create the standalone controller.js file for my future reference. These instructions work for me on OSX 10.6.

  1. Go to the JavaScriptMVC downloads page on Github
  2. Download the latest version (3.0.0a0 as of this writing) of the framework and unzip it
  3. Open a terminal window and cd to the directory created when you unzipped the file
  4. Run steal/js steal/compress/plugin.js jquery/controller
  5. You should now have a new controller.js file in the directory; you're all set!

Loading mentions Retweet

Filed under  //  howto   javascript   javascriptmvc   jquery  
Comment (1)
Posted 5 months ago

Demystifying custom events in jQuery

This article originally appeared in the May 2009 issue of JSMag. We’re all familiar with the basic events — click, mouseover, focus, blur, submit, etc. — that we can latch on to as a user interacts with the browser. Custom events open up a whole new world of event-driven programming. In this article, we’ll use jQuery’s custom events system to make a simple Twitter search application, but the general concepts should apply to any framework that supports custom events. I confess: it took me a long time to decide to learn about custom events. The built-in events seemed to suit my needs just fine, and it was difficult to understand why I’d want to start adding my own. Boy, was I missing out. It turns out that custom events offer a whole new way of thinking about event-driven JavaScript. Instead of focusing on the element that triggers an action, custom events put the spotlight on the element being acted upon. This brings a bevy of benefits, including:

  • behaviors of the target element can easily be triggered by different elements using the same code;
  • behaviors can be triggered across multiple, similar, target elements at once; and
  • behaviors are more clearly associated with the target element in code, making code easier to read and maintain
Why should you care? An example is probably the best way to explain. Suppose you have a lightbulb in a room in a house. The lightbulb is currently turned on, and it’s controlled by two three-way switches and a clapper, as shown here:
Triggering the clapper or either of the switches will change the state of the lightbulb. The switches and the clapper don’t care what state the lightbulb is in; they just want to change the state. Without custom events, you might write some code like this:
$('.switch, .clapper').click(function() {
        var $light = $(this).parent().find('.lightbulb');
        if ($light.hasClass('on')) {
                $light.removeClass('on').addClass('off');
        } else {
                $light.removeClass('off').addClass('on');
        }
});
With custom events, your code might look more like this:
$('.lightbulb').bind('changeState', function(e) {
        var $light = $(this);
        if ($light.hasClass('on')) {
                $light.removeClass('on').addClass('off');
        } else {
                $light.removeClass('off').addClass('on');
        }
});
$('.switch, .clapper').click(function() { 
        $(this).parent().find('.lightbulb').trigger('changeState');
});
This last bit of code is not that exciting, but something important has happened: we’ve moved the behavior of the lightbulb to the lightbulb, and away from the switches and the clapper. Let’s make our example a little more interesting. We’ll add another room to our house, along with a master switch, as shown here:
If there are any lights on in the house, we want the master switch to turn all the lights off; otherwise, we want it to turn all lights on. To accomplish this, we’ll add two more custom events to the lightbulbs: turnOn and turnOff. We’ll make use of them in the changeState custom event, and use some logic to decide which one the master switch should trigger:
$('.lightbulb').
bind('changeState', function(e) {
        var $light = $(this);
        if ($light.hasClass('on')) {
                $light.trigger('turnOff');
        } else {
                $light.trigger('turnOn');
        }
}).
bind('turnOn', function(e) {
        $(this).removeClass('off').addClass('on');
}).
bind('turnOff', function(e) {
        $(this).removeClass('off').addClass('on');
});

$('.switch, .clapper').click(function() { 
        $(this).parent().find('.lightbulb').trigger('changeState');
});

$('#master_switch').click(function() {
        if ($('.lightbulb.on').length) {
                $('.lightbulb').trigger('turnOff');
        } else {
                $('.lightbulb').trigger('turnOn');
        }
});
A bit more interesting, huh? Note how the rules about what the master switch does belongs to the master switch; the rules about how a lightbulb turns on and off belong to the lightbulb. It’s also worth noting that the master switch is able to affect all of the lightbulbs without having to iterate over them — it just triggers an event on all elements that have the class “lightbulb”. This isn’t critical to the example, but if we were to perform more elaborate manipulations of each lightbulb than adding and removing a class, this would be an important benefit of custom events, as you’ll see in the rest of the article. (If you’re accustomed to object-oriented programming, you may find it useful to think of custom events as methods of objects. Loosely speaking, the object to which the method belongs is created via the jQuery selector. Binding the changeState custom event to all $('.light') elements is akin to having a class called Light with a method of changeState(), and then instantiating new Light objects for each element with a classname of light.)

jQuery Event Primer

Before we dive in, a couple of things we need to recap. In the world of custom events, there are two important jQuery methods: .bind() and .trigger(). I encourage you to read the jQuery docs for details, but basically:
  • The .bind() method takes an event type and an event handling function as arguments. Optionally, it can also receive data, which will be available to the event handling function in the data property of the event object. The event handling function always receives the event object as its first argument.
  • The .trigger() method takes an event type as its argument. Optionally, it can also take an array of values. The first item in the array will be the second argument passed to the event handling function (after the event object).
Confused? I don’t blame you. Read on and I’ll try to clear some things up.

Our Mission

To demonstrate the power of custom events, we’re going to create a simple tool for searching Twitter. The tool will offer several ways for a user to add search terms to the display: by entering a search term in a text box, by entering multiple search terms in the URL, and by querying Twitter for trending terms. The results for each term will be shown in a results container; these containers will be able to be expanded, collapsed, refreshed, and removed, either individually or all at once. When we’re done, it will look like this:

The Setup

We’ll start with some basic HTML:
Twitter Search

Search Results for

This gives us a container (#twitter) for our widget, a template for our results containers (hidden via CSS), and a simple form where users can input a search term. (For the sake of simplicity, we’re going to assume that our application is JavaScript-only and that our users will always have CSS.) First, the setup. We’ll build an $actions object that we’ll use later to create the buttons in each results container, and we’ll also create a global search_terms object so we can store a list of search terms that are being displayed on the page. Next, we’ll do our custom event binding. There are two types of objects we’ll want to act on: the results containers, and the Twitter container.

The Results Containers

The results containers are the heart of the application. We’ll create a setupResults() plugin that will prepare each results container once it’s added to the Twitter container. Among other things, it will bind the custom events for each container and add the action buttons at the top right of each container. Each results container will have the following custom events:
  • The refresh event will mark the container as being in the “refreshing” state, and fire the $.getJSON() request to fetch the data for the search term.
  • The populate event will receive the returned JSON data and use it to populate the container.
  • The remove event will remove the container from the page after the user verifies the request to do so. Verification can be bypassed by passing true as the second argument to the event handler. The remove event also removes the term associated with the results container from the global search_terms object.
  • The collapse event will add a class of collapsed to the container, which will hide the results via CSS. It will also turn the container’s “Collapse” button into an “Expand” button.
  • The expand event will remove the collapsed class from the container. It will also turn the container’s “Expand” button into a “Collapse” button.
The plugin is also responsible for adding the action buttons to the container by cloning the $actions object that was created earlier. It binds a click event to each action’s list item, and uses the list item’s class to determine which custom event will be triggered on the corresponding results container.
// we'll use this every time we add a new results panel,
// so let's build it once and cache it in $actions
var $actions = $('
    '); $('
  • Refresh
  • ').appendTo($actions); $('
  • Remove
  • ').appendTo($actions); $('
  • Collapse
  • ').appendTo($actions); // this is where we'll keep track of which search terms // are shown on the page already var search_terms = {}; Here's the setupResults plugin:
    $.fn.setupResults = function(settings) {
            return $(this).each(function() {
                    
                    var $results = $(this);
                    var $actions = settings.actions;
                    var term = settings.term;
    
                    // change the "Search results for" text
                    $results.find('span.search_term').text(term);
    
                    // bind custom events for results box
                    $results. 
            
                    // the "refresh" event fetches 
                    // the latest content for the term
                    bind('refresh', function(e) {
                            // indicate that the results are refreshing
                            var $this = $(this).addClass('refreshing');
    
                            $this.find('p.tweet').remove();
                            $results.append('Loading ...');
    
                            // get the twitter data using jsonp
                            $.getJSON(
                                    'http://search.twitter.com/search.json?q=' +                                         escape(term) + '&rpp=5&callback=?', 
                                    function(json) { 
                                            $this.trigger('populate', [ json ]); 
                                    }
                            );
                    }).
                    
                    // the "populate" event takes results 
                    // in json format 
                    // and populates the results container
                    bind('populate', function(e, json) {
                            var results = json.results;
                            var $this = $(this);
                            
                            $this.find('p.loading').remove();
    
                            $.each(results, function(i,result) {
                                    var tweet = '' + 
                                            '' +
                                            result.from_user + 
                                            ': ' +
                                            result.text + 
                                            ' ' + 
                                            result.created_at + 
                                            '' +
                                    '';
                                    $this.append(tweet);
                                            });
    
                                            // indicate that the results 
                                            // are done refreshing
                                            $this.removeClass('refreshing');
                                    }).
                                                    // the remove event removes 
                                                    // the results from the page
                                                    // after the user confirms the action
                                                    bind('remove', function(e, force) {
                                                            // allow forced removal without confirmation
                                                            if (
                                                                    !force &&         
                                                                    !confirm('Remove panel for term ' + term + '?')
                                                            ) {
                                                                    return;
                                                            }
                                                            $(this).remove();
    
                                                            // indicate that we no longer 
                                                            // have a panel for the term
                                                            search_terms[term] = 0;
                                                    }).
    
                                                    // the collapse event collapses the results so only the
                                                    // header of the results section is showing
                                                    bind('collapse', function(e) {
                                                            $(this).find('li.collapse').removeClass('collapse')
                                                                    .addClass('expand').text('Expand');
    
                                                            $(this).addClass('collapsed');
                                                    }).
    
                                                    // the expand event
                                                    bind('expand', function(e) {
                                                            $(this).find('li.expand').removeClass('expand')
                                                                    .addClass('collapse').text('Collapse');
    
                                                            $(this).removeClass('collapsed');
                                                    });
    
                                                    if ($actions && $actions.length) {
                                                            // add a clone of $actions to the results panel
                                                            var $a = $actions.clone().prependTo($results);
    
                                                            // use the class of each action to figure out 
                                                            // which event it will trigger on the results panel
                                                            $a.find('li').click(function() {
                                                                    // pass the li that was clicked to the function
                                                                    // so it can be manipulated if needed
                                                                    $results.trigger(
                                                                            $(this).attr('class'), [ $(this) ]
                                                                    );
                                                            });
                                                    }
                                            });
                                    };

    The Twitter Container

    The Twitter container itself will have just two custom events:
    • The getResults event will receive a search term. It will check the global search_terms object to determine whether there’s already a results container for the term; if not, it will add a results container using the results template (div.template), set up the results container using the setupResults() plugin discussed above, and then trigger the refresh event on the results container in order to actually load the results. Finally, it will store the search term in the global search_terms object, so the application knows not to re-fetch the term.
    • The getTrends event will query Twitter for the top 10 trending terms. It will iterate over them and trigger the widget’s getResults event for each of them, thereby adding a results container for each term. Here you can see how we go about passing data to a triggered event.
    The Twitter container bindings are shown here:
    $('#twitter').
    bind('getResults', function(e, term) {
            // make sure we don't have a box for this term already
            if (!search_terms[term]) { 
                    var $this = $(this);
                    var $template = $this.find('div.template');
    
                    // make a copy of the template div
                    // and insert it as the first results box
                    $results = $template.clone().
                            removeClass('template').
                            insertBefore($this.find('div:first')).
                            setupResults({
                                    'term' : term,
                                    'actions' : $actions
                            });
    
                    // load the content using the "refresh" 
                    // custom event that we bound to the results container
                    $results.trigger('refresh');
                    search_terms[term] = 1;
            }
    }).
    
    bind('getTrends', function(e) {
            var $this = $(this);
            $.getJSON('http://search.twitter.com/trends.json?callback=?',                 function(json) {
                            var trends = json.trends; 
                            $.each(trends, function(i, trend) {
                                    $this.trigger('getResults', [ trend.name ]);
                            });
                    });
    });
    So far, we’ve written a lot of code that does approximately nothing, but that’s OK. By specifying all the behaviors that we want our core objects to have, we’ve created a solid framework for rapidly building out the interface. Let’s start by hooking up our text input and the “Load Trending Terms” button. For the text input, we’ll make the form submission stop in its tracks using e.preventDefault(), then capture the term that was entered in the input and pass it to the Twitter widget’s getResults event. (Again, you can see how we go about passing data to a triggered event.) Clicking the “Load Trending Terms” will simply trigger the Twitter widget’s getTrends event:
    $(document).ready(function() {
    
    $('form').submit(function(e) {
            e.preventDefault();
            var term = $('#search_term').val();
            $('#twitter').trigger('getResults', [ term ]);
    });
    
    $('#get_trends').click(function() {
            $('#twitter').trigger('getTrends'); 
    });
    
    });
    Just entering a search term into a text box is boring, of course — the following code shows how we can capture search terms from the URL’s hash (e.g., http://foo.com/index.html#foo,bar):
    $(document).ready(function() {
    
    // pass search terms via URL hash 
    if (document.location.hash) {
            var terms = document.location.hash.split(',').reverse();
            $.each(terms, function(i,term) {
                    $('#twitter').trigger('getResults', [ term ]);
            });
    }
    
    });
    By adding a few buttons with the appropriate IDs, we can make it possible to remove, collapse, expand, and refresh all results containers at once, as shown below. For the remove button, note how we’re passing a value of true to the event handler as its second argument, telling the event handler that we don’t want to verify the removal of individual containers.
    $(document).ready(function() {
    
    $('#refresh').click(function(e) {
            $('#twitter div.results').trigger('refresh'); 
    });
    
    $('#expand').click(function(e) {
            $('#twitter div.results').trigger('expand'); 
    });
    
    $('#collapse').click(function(e) {
            $('#twitter div.results').trigger('collapse'); 
    });
    
    $('#remove').click(function(e) {
            if (confirm('Remove all results?')) {
                    $('#twitter div.results').
                            trigger('remove', [ true ]);
            }
    });
    });
    It should be noted that you don’t have to choose one or all results containers — really, you can choose any results containers you want to affect. For example, if for some (strange) reason you wanted to refresh only the first and last results containers, you could do:
    $('div.results:first, div.results:last').
            trigger('refresh');
    You can see the entire application, including the full HTML and CSS, at http://www.rebeccamurphey.com/jsmag/custom-events/.

    Conclusion

    Custom events offer a new way of thinking about your code: they put the emphasis on the target of a behavior, not on the element that triggers it. If you take the time at the outset to spell out the pieces of your application, as well as the behaviors those pieces need to exhibit, custom events can provide a powerful way for you to “talk” to those pieces, either one at a time or en masse. Once the behaviors of a piece have been described, it becomes trivial to trigger those behaviors from anywhere, allowing for rapid creation of and experimentation with interface options. Finally, custom events can enhance code readability and maintainability, by making clear the relationship between an element and its behaviors.

    Learn More

    Loading mentions Retweet

    Filed under  //  howto   javascript   jquery  
    Comments (7)
    Posted 9 months ago

    Dojo Confessions (Or: How I gave up my jQuery Security Blanket and Lived to Tell the Tale)

    This is a reprint of an article that originally appeared in the October issue of JSMag. I recently had the opportunity to architect the front-end of a new web application from scratch, and after years of using jQuery, I decided to give Dojo a try. For a variety of reasons — not least of which was Dojo’s approach to code organization and dependency management — I thought this would be a good project to get some real-world Dojo experience under my belt. What follows is an overview of Dojo from the perspective of an avid jQuery user. I’ve been using jQuery for years. Its simplicity is seductive; after a while, it kind of writes itself. And maybe that was my problem: I was ready to try something new. Many months ago, I wrote on Twitter that I was interested in learning more about Dojo. Rey Bango, jQuery evangelist, wrote back, and put me in touch with Pete Higgins, the Dojo project lead. Pete proceeded to lobby me for months to give his “unified toolkit” a try. I dabbled. I read the docs. Pete plied me with sample code to show me what I was missing, and even drove to North Carolina to lead a Dojo camp. In August, I decided it was time to stop dabbling and dive in. I’d just finished writing some essentially library-less JavaScript for a web-based iPhone application, a task that left me much more confident in my knowledge of JavaScript. With the mobile site out of the way, the client was ready to build the desktop version, and I would be in charge of the front end. It was time to choose a library; this time, I chose Dojo.

    Deciding on Dojo

    The decision to try a new library on a client project was a tough one — I knew that I could write the basic functionality of the site using jQuery a whole lot faster than I would write it with Dojo, simply because I knew my way around jQuery so much better. Using Dojo would mean I’d be looking up a lot of things, from simple stuff like how to work with DOM selections, to more complicated tasks like how to structure my code for the purposes of reuse and abstraction. As my work on the project progressed and the deadline neared, I had plenty of second thoughts. A few things convinced me to stick with Dojo when the going got tough:

    • Code organization patterns: Dojo provides pretty clear guidance on structuring both your features and your codebase. I’ve given a lot of thought to organizing jQuery code. I wrote an article on the topic for JSMag and gave a presentation on the topic at the jQuery conference. I was eager to try a library that explicitly answers the organization question.
    • Class inheritance: I knew from the start that I was going to use a lot of interaction patterns over and over. I wanted to be able to write those patterns in a way that would let me use them across features while still staying DRY. The class inheritance provided by dojo.declare() was an elegant, easy-to-use solution to the problem.
    • Dependency management: Being able to easily manage dependencies was a huge draw for me; it promotes reuse and abstraction in a big way. Dojo’s dependency management would also pave the way to easily building production-ready files that would combine all the necessary functionality for a given type of page, reducing the number of HTTP requests required.
    • Service method descriptions: This particular application relied on XHRs (AJAX) in a big way. Dojo’s service method description approach would let me manage the URLs and parameters for those requests in a single place, keeping pieces that might change separate from the core code. Eventually, theoretically, the server-side code could actually generate this SMD file automatically. More on this in a bit.
    • Templating: All the XHR responses were JSON, which I’d need to turn into HTML. jQuery has templating plugins to solve this problem, so this wasn’t really a differentiating factor, but nonetheless it was going to make my life easier. I could maintain the templates for turning JSON into HTML separately from my JavaScript, and even programmatically choose the template depending on the situation.
    • The meaning of this: When binding a function to an event on an element in jQuery, this inside the function refers to the element that triggered the event. This is arguably desirable for simple code, but when you start organizing your code into objects with methods and you want this to refer to the object, not the element, it can get painful. The dojo.hitch() method lets you cleanly change the meaning of this for any given function, and it’s transparently rolled into other methods, such as dojo.connect() for event binding.
    • Documentation and support: Dojo has a reputation for poor documentation, and to some extent it’s deserved. Their documentation is a whole lot harder to use than jQuery’s because, at first glance, it’s quite a bit more scattered and substantially more API-based than task-based. However, once I figured out where to look for the docs I needed, finding answers to my questions was pretty painless. I also leaned heavily on some experienced Dojo developers for guidance and support, and dropped in to the ever-helpful #dojo IRC channel on Freenode if I got stuck.

    Getting Started

    The first step was assembling my new toy. I opted to use the library via Google’s CDN so I could get up and running as quickly as possible. After that, it was time to figure out how I’d organize my files. Dojo actively supports namespaces for components, which means you can put your application files in one or more directories and associate your namespaces with those directories. I created a high-level controller file in the root /js directory; it would be responsible for figuring out which functionality was required for a given page (a decision I’ll eventually revisit). Then, I created a directory inside the root /js directory, where I’d put all of the individual files for the various components. Because I was using the Google-hosted version of Dojo, I had to put a script tag in my header template, before I included the base dojo.js, telling Dojo where to find my local files:

    Finally, I included a line in my controller file to tell Dojo where to find the namespace I’d be using: dojo.registerModulePath('myNamespace', '../js/myNamespace'); Figuring out all of these pieces may have been the hardest part of making the switch to Dojo — it was a whole lot more setup than I was used to with jQuery, and though it is all documented, it took a bit of effort to find the details and to get the paths set up correctly. The time it took to get everything working properly was time that I spent wondering whether I’d made a good decision. Once it was working, it was time to write some actual code and try to answer that question.

    Get Some Elements, Do Something With Them

    Those words sum up the jQuery paradigm. In jQuery, you query the DOM using a CSS selector, and the result of that query is a jQuery object, which you can then operate on using method chains. It’s fairly rare in jQuery to work directly with a DOM element. While Dojo supports this paradigm through its dojo.query() method and the NodeList it returns, it’s common in Dojo to work directly with a DOM element rather than a NodeList. My initial inclination was to stick with what I knew from jQuery, and to use dojo.query() to get everything I wanted to work with. As I dug in, though, I discovered that it could actually be just as elegant (and less expensive) to work directly with DOM elements, even though they didn’t come with any of the magic of a jQuery object. The syntax for doing so was a bit different — for example, dojo.addClass(myDomElement, ‘foo’) instead of $(myDomElement).addClass(‘foo’) — but the more code I wrote, the more frequently and easily I found myself using the dojo.addClass syntax instead. Embracing this approach was especially valuable when it came to methods that returned something. For example, the dojo.connect() method (used to connect events to elements, similar to $().bind()) returns a connection object, which can be stored and disconnected later without having to know which element the event was attached to. This is, in a word, awesome. It’s also an example of how Dojo requires you to think somewhat differently about how you write your JavaScript.

    Returning a Result For the Win

    Along those lines, I had to get used to the fact that a lot of Dojo methods returned objects that I could talk to later. For example, dojo.animateProperty() created an animation object which could later be play()’d. All of the XHR methods — and asynchronous methods in general — returned a “deferred” object, to which I could later add success and failure callbacks. jQuery does return the native XHR object from its $.ajax() method, so you can technically add callbacks there too. What I liked about Dojo’s deferred approach is that it provides a common, simple interface for interacting with all asynchronous operations, and even lets you define your own. Getting the hang of how to take advantage of these things took some doing, coming from the more procedural, chained world of jQuery, where just about everything returns a jQuery object. Soon, though, I was setting up animations long before I was ready to play them, and adding callbacks to XHRs after they started.

    SMDs: A Unified Way to Talk to the Server

    One thing I really wanted to try with Dojo was making use of Service Method Descriptions, or SMDs. An SMD file basically contains information about all of the services provided by a resource. In my case, the resource was the server-side application, which I’d be communicating with to request JSON data. By creating an SMD file, and then instantiating a new Service based on that file, I could create a single place for managing all the paths and parameters I’d use to get what I needed from the server. When I asked the server for something, the Service I created would return a deferred object, to which I could attach callbacks. In the background, Dojo was just running an XHR, but my individual classes didn’t have to worry about the details — I just had to worry about the name of the method and the parameters it required, and the Service I’d defined and instantiated would take care of the rest. For my initial work, I just created the SMD file by hand, but eventually it’s easy to see how the SMD could be generated by the server-side application itself. Here's a sample SMD (normally an SMD would have a lot more services, obviously):

    {
            transport        :         'GET',
            envelope         :         'PATH',
            target                 :         '/json',
            
            services         :         {
                    callouts : {
                            parameters : [
                                    { name : 'callouts', type : 'string' }
                            ],
                            returns : 'object'
                    }
            }
    }

    And here's some abbreviated code that makes use of the SMD:

    dojo.provide('myProject.Callouts');
    
    dojo.require('dijit._Widget');
    dojo.require('myProject.Services');
    dojo.require('dojox.rpc.Service');
    
    dojo.declare('myProject.Callouts', dijit._Widget, {
            cache : {},
            
            services : new dojox.rpc.Service(dojo.moduleUrl('smd', 'json.smd')),
            
            postCreate : function() {
                    // ...
            },
            _lookup : function() {
                    var val = this.domNode.value;
                    
                            if (this.cache[val]) {
                                    this._handleResult(this.cache[val]);
                            } else {
                                    var c = this.services.json.callouts({
                                            'callouts' : val
                                    });
                                    
                                    c.addCallback(dojo.hitch(this, 
                                            function(resp) {
                                                    this.cache[val] = resp;
                                            })
                                    );
                                    
                                    c.addCallback(dojo.hitch(this, 
                                            '_handleResult'));
                            }
            },
            
            _handleResult : function(resp) {
                    // ...
            }
    });

    Dependency Management and Building

    I love jQuery, I do, but this is an area where it is sorely lacking. It offers little guidance on how to structure your codebase, how to ensure that everything you need is loaded, or how to build your code into production-ready files. Dojo really shines here, but again, it takes a bit of getting used to. The base dojo.js includes a ton of basic functionality, but I had to quickly learn to dojo.require() additional functionality as I needed it. In jQuery, you’d do this simply by adding another script tag to your HTML; dojo.require() basically does this for you programmatically, but checks to see that the required code hasn’t already been included first. This means each of your components can require exactly what it needs, and it’s perfectly safe to require the same thing more than once. The flip side of this is that if you forget to require something you need, or if you require it using the wrong name, it’s not always clear exactly where you made the mistake. Each file that will be dojo.require()’d begins with a dojo.provide() statement, telling the dependency management system that it has, indeed, found the right file. After that, the file can go on to dojo.require() its own dependencies, which Dojo loads before proceeding with the rest of the file’s code. The dojo.provide() method also sets up the object chain along the path; for example, if you dojo.provide(‘a.b.c.d’), you can then safely do a.b.c.d.e = { ... }. When it comes time to assemble your individual files into production-ready, combined, minified files, Dojo’s build system is able to parse dojo.require() statements and automatically include the appropriate files; with jQuery, this is a much more manual process that can be difficult to maintain. Creating a build wasn’t as straightforward as I’d hoped it would be, and I stumbed a lot along the way. It took a bit of doing to get all of the paths just right, and to figure out how to have a repeatable build process that we could roll up into our full release process. The payoff was big, though: I could keep my files organized how I wanted them, but only serve one file in production. The build system figured out the steps in between.

    Organization, Inheritance and Abstraction

    As I mentioned above, code organization has been a big issue for me with jQuery. I’ve developed some patterns that I use to keep my code sane, but plenty of other jQuery developers have not, which can make working with other people’s code rather painful. While it’s certainly possible to write procedural, disorganized code with Dojo, dojo.declare() provides a clear way to avoid it. I talked earlier about creating a namespace for my code and an associated directory. When it came time to start writing, I created individual files in that directory for each component on the page. Inside each file, I indicated which component the file was providing via dojo.provide(), loaded any dependencies via dojo.require(), and then created a class in the namespace using dojo.declare(). The dojo.declare() method takes three arguments: the name of the class you want to create (‘myNamespace.Thinger’), other classes you want to “mix in” to your new class (if any — this argument can be null, a single class, or an array of classes), and, lastly, an object that defines the class methods and properties. The result is a class that can be instantiated using new myNamespace.Thinger(); the object that’s created encapsulates all the behaviors and states associated with a particular Thinger, and you can have as many instances of Thinger as you want. The mixing in thing is huge, because it lets you have a class that incorporates methods defined in another class. For example, I created a class called myNamespace.Toggler that would show either the first item in a list or all of the items in a list; clicking on the first list item would toggle between the behaviors. Once the myNamespace.Toggler class was created, other classes could inherit its behavior simply by passing a reference to the myNamespace.Toggler class as the second argument of dojo.declare(). I was able to encapsulate the Toggler behavior in a reusable way, and keep the code for the classes that inherited the Toggler behavior nice and clean.

    Event Management

    I mentioned earlier that Dojo has a slightly different take on event binding than jQuery. I should also say that you can bind events to entire NodeLists (the result of dojo.query()) if you want, using .connect() (or convenience methods like .onclick(), etc.). However, if you want a reference to the connection for later use, dojo.connect() is your friend. I created a component using dojo.declare() that was responsible for managing user interaction with a star rating UI element. I used dojo.connect() to hook up some mouseover/mouseout behaviors to the element, and stored the returned connections as properties of the component. When the element was clicked, I registered the rating, and I wanted the mouseover/mouseout behaviors to go away; eliminating them was simply a matter of dojo.disconnect()-ing the stored connections.

    Publish and Subscribe

    With all of the XHR traffic occurring on the page, I wanted a way to shut it off if the user was inactive for a little while, but I didn’t want to write the code for handling that inside every component that used XHR. Dojo’s pub/sub tools offered the perfect answer. I created a new component whose sole purpose was to watch for inactivity; when it discovered inactivity, it would dojo.publish(‘/user/inactive’). Then, other components — the ones I’d already written, and ones I write in the future — could subscribe to the /user/inactive topic and react accordingly. Pub/sub is an excellent way to allow this sort of abstract communication between components. The component that publishes a “topic” doesn’t care who’s listening; the component that subscribes to a topic doesn’t care which component sent it. It’s another example of how Dojo leads you to think a bit differently about how you architect your applications — knowing about pub/sub can help you write much more loosely coupled code.

    What I Missed from jQuery

    jQuery’s event delegation-related methods .live() and .is() were hard to live without. There’s a reasonable way to mimic .is(), but no out-of-the-box replacement for .live() — you end up writing your event delegation yourself. Dojo does have dojo.behavior(), which automatically binds event handlers to elements that match a given selector as they’re added to the page; however, the event handlers are bound to individual elements, and there’s no way to provide a context to the selector that you pass to dojo.behavior(). This may be my noob-ness talking, or maybe I’m just used to the error messages I’d see when I did something wrong with jQuery, but I often found myself feeling that the error messages from Dojo were too cryptic. Worse, sometimes I’d do something wrong and it would seem to fail silently. I spent a lot more time in Firebug tracking down the errors of my ways. In general, the thing I really missed from jQuery was the “it just works” aspect of the library. I expect that with time I’ll feel that way about Dojo, but in the meantime there are definitely growing pains. I had to constantly remind myself that the way to solve a problem in Dojo might not be the same way I’d solve it in jQuery. More than once I rewrote a slew of code when I discovered some Dojo methodology or approach I hadn’t known about before.

    Conclusion

    Lest Rey worry that he never should have introduced me to Pete in the first place, fear not: I don’t expect to give up jQuery anytime soon. If anything, I’m excited to see how the library and the community mature and start answering some of the organization and dependency management questions I mentioned above. As a library, jQuery most definitely has its place; it has virtually no barriers to entry and it has helped usher in an era where it’s dead-simple to create rich, interactive websites. Deciding to use Dojo instead was something of a gamble. I had to convince the project lead that it was a good decision, which was challenging considering the popularity of the jQuery library. He asked lots of pointed questions about the maintainability of the code if I were to leave the project, and those questions were well deserved. If anything, though, I think that choosing Dojo has actually increased the maintainability of the code by presenting clear patterns for organization, abstraction, and dependency management. Did it take a while to come up to speed with Dojo? For sure. Will a jQuery developer off the street be able to jump right in to the code I wrote? Possibly not. At the end of the day, though, it is just JavaScript, and any skilled JavaScript developer should be able to find their way around. They’ll almost certainly find, given an hour or two, that the code I wrote is easier to follow than some of the jQuery code I’ve run into that doesn’t make use of good organizing principles. In the meantime, I hope to be working on the project for a while to come, and I expect the trouble I went through to come up to speed on Dojo will pay big dividends as the application I’m working on grows and matures.

    Learn More

    Loading mentions Retweet

    Filed under  //  dojo   jquery  
    Comments (8)
    Posted 9 months ago

    Introducing yayQuery -- A jQuery podcast

    yayQuery 1.0 from yayQuery on Vimeo.

    If you'd like a download: mp3 audio (30mb), mp4 video (94mb), ogg video (61mb), Vimeo In this our first episode of the official yayQuery Podcast, Paul Irish, Adam J. Sontag, Alex Sexton and I stayed up way too late on Monday night and had ridiculous amounts of fun talking about:
    • Underscore.js, the new functional programming JavaScript library.
    • The demise of Thickbox (and some good, modern alternatives).
    • Using (or not using) jQuery for mobile development.
    • Paul Irish's antipattern of the week: css(key, value)
    • $var vs. var (Hungarian Notation)
    Perhaps because it was so late when we finished, there was also chair dancing. Make sure you don't miss it, but don't fast-forward to the end or you'll miss the good stuff. This is our first try with this, and who knows what will become of it, but we're very grateful for any and all feedback. You can find us on Twitter @yayQuery, or on the #jquery IRC channel. Enjoy!

    Loading mentions Retweet

    Filed under  //  jquery   speaking  
    Comment (1)
    Posted 10 months ago