51

I've been struggling lately with understanding the best way to organize jQuery code. I asked another question earlier and I don't think I was specific enough (found in this question here).

My problem is that the richer you make an application, the quicker your client side gets out of control. Consider this situation...

//Let's start some jQuery
$(function() {        
    var container = $("#inputContainer");

    //Okay let's list text fields that can be updated
    for(var i=0; i < 5; i++) {

        //okay let's add an event for when a field changes
        $("<input/>").change(function() {

            //okay something changed, let's update the server
            $.ajax({
                success:function(data) {

                    //Okay - no problem from the server... let's update
                    //the bindings on our input fields
                    $.each(container.children(), function(j,w) {

                        //YIKES!! We're deep in here now!!
                        $(w).unbind().change(function() {

                            //Then insanity starts...

                        }); // end some function

                    }); //end some loop

                } // what was this again?

            }); //ending something... not sure anymore

        }).appendTo(container); //input added to the page... logic WAY split apart

    }; //the first loop - whew! almost out!

});  //The start of the code!!

Now this situation isn't too far from impossible. I'm not saying this is the right way to do it, but it's not uncommon to find yourself several levels down into a jQuery command and starting to wonder how much more logic can add before the screen begins to melt.

My question is how are people managing this or organizing to limit the complexity of their code?

I listed how I'm doing it in my other post...

Community
  • 1
  • 1
hugoware
  • 33,265
  • 24
  • 58
  • 70

8 Answers8

49

Just want to add to what was mentioned previously that this:

$.each(container.children(), function(j,w) {
    $(w).unbind().change(function() { ... });
});

can be optimized to:

container.children().unbind().change(function() { ... });

It's all about chaining, a great way to simplify your code.

John Resig
  • 33,649
  • 3
  • 26
  • 19
18

So far, I do it like this:

// initial description of this code block
$(function() {        
    var container = $("#inputContainer");

    for(var i=0; i < 5; i++) {
        $("<input/>").changed(inputChanged).appendTo(container);
    }; 

    function inputChanged() {
        $.ajax({
            success: inputChanged_onSuccess
        });
     } 

     function inputChanged_onSuccess(data) {
        $.each(container.children(), function(j,w) {
          $(w).unbind().changed(function() {
             //replace the insanity with another refactored function
          });
        });
      }
});

In JavaScript, functions are first-class objects and can thus be used as variables.

David Alpert
  • 3,161
  • 1
  • 21
  • 19
  • 1
    I like this approach too - keeps everything private. Unless you want some of the methods to be public;) – Peter Bailey Oct 30 '08 at 21:40
  • agreed; this approach doesn't leave behind any cruft in the global namespace as it encapsulates everything inside an anonymous closure. jQuery's functional style tends towards that pattern. Crockford's OOP pattern in BaileyP's answer is useful when you need a reference to your code block later. – David Alpert Oct 30 '08 at 21:44
8

Well, for one, having a good IDE that understands javascript can help tremendously, even if just to identify matching demarcations (braces, parens, etc).

If your code starts to really get that complex, consider making your own static object to organize the mess - you don't have to work so hard to keep everything anonymous.

var aCustomObject = {
    container: $("#inputContainer"),
    initialize: function()
    {
        for(var i=0; i < 5; i++)
        {
            $("<input/>").changed( aCustomObject.changeHandler );
        }
    },
    changeHandler: function( event )
    {
        $.ajax( {success: aCustomObject.ajaxSuccessHandler} );
    },
    ajaxSuccessHandler: function( data )
    {
        $.each( aCustomObject.container.children(), aCustomObject.updateBindings )
    },
    updateBindings: function( j, w )
    {
        $(w).unbind().changed( function(){} );
    }
}
aCustomObject.initialize();
Peter Bailey
  • 101,481
  • 30
  • 175
  • 199
  • I want to vote you up for mentioning the benefit of a good IDE - but I want to vote you down for suggesting using Objects for code organization as opposed to their intended function. – matt lohkamp Nov 05 '08 at 12:55
  • on the other hand, maybe you don't mean to use Objects as organizational elements to the exclusion of other considerations, so I'll give you the benefit of the doubt. – matt lohkamp Nov 05 '08 at 12:56
  • 5
    The purpose of the object is not necessarily for organization, although that's a nice ancillary benefit, but to keep the global namespace consumption as small as possible. I don't think that's against their "intended purpose" at all. That type of thinking is desperately limiting. – Peter Bailey Nov 05 '08 at 16:55
4

Somebody wrote a post on the similar topic.

jQuery Code Does not have to be Ugly

For instance, the author, Steve Wellens, suggests to not use anonymous functions, as it makes code harder to read. Instead, push the function reference into the jQuery methods, like so:

$(document).ready(DocReady);

function DocReady()
{       
    AssignClickToToggleButtons();
    ColorCodeTextBoxes();
}

Another takeaway from the article is to assign a jQuery object to a concrete variable, which makes the code look cleaner, less dependent on the actual jQuery object, and easier to tell what a certain line of code is doing:

function ColorCodeTextBoxes()
{
    var TextBoxes = $(":text.DataEntry");

    TextBoxes.each(function()
    {
        if (this.value == "")
            this.style.backgroundColor = "yellow";
        else
            this.style.backgroundColor = "White";
    });
}
jmort253
  • 32,054
  • 10
  • 92
  • 114
Irfan
  • 486
  • 6
  • 12
  • I originally flagged this as not an answer but then realized this is something I have enough experience with to fix myself, so I edited this to bring in some content from the link, so that if it were to break, this post would still be useful to future visitors. Hope this helps! – jmort253 May 10 '14 at 02:44
4

In my opinion the method described by BaileyP is what I use to start off with then I normally abstract everything into more re-usable chunks, especially when some functionality expands to the point where it's easier to abstract it into a plugin then have it specific to one site.

As long as you keep the large blocks of code in a seperate file and coded nicely you can then end up with some really clean syntax.

// Page specific code
jQuery(function() {
    for(var i = 0; i < 5; i++) {
         $("<input/>").bindWithServer("#inputContainer");
    }
});

// Nicely abstracted code
jQuery.fn.bindWithServer = function(container) {
     this.change(function() {
             jQuery.ajax({
                 url: 'http://example.com/',
                 success: function() { jQuery(container).unbindChildren(); }
             });
     });
}
jQuery.fn.unbindChildren = function() {
    this.children().each(function() {
        jQuery(this).unbind().change(function() {});
    });
}
user32924
  • 41
  • 1
2

Stick some of the anon functions into global scope functions (or your own "namespace" object), especially the re-used functions, and it begins to look less like what you posted. Kind of like what you linked to.

Josh
  • 16,016
  • 6
  • 43
  • 62
2

I described my approach in your other post. Short form:

  • do not mix javascript and HTML
  • use classes (basically start to see your application as a collection of widgets)
  • only have a single $(document).ready(...) block
  • send jQuery instances into your classes (instead of using plugins)
Community
  • 1
  • 1
Jason Moore
  • 6,977
  • 1
  • 40
  • 45
2

Use http://coffeescript.com/ ;)

$ ->
  container = $ '#inputContainer'
  for i in [0...5]
    $('<input/>').change ->
      $.ajax success: (data) ->
        for w in container.children()
          $(w).unbind().change ->
            alert 'duh'
Aldo Bucchi
  • 415
  • 4
  • 10
  • Don't really understand how this will help the OP. But +1 anyway for showing me something new and interesting. – Robert Harvey Oct 28 '10 at 04:31
  • I answered this more than 3 years ago and this counts as negative reputation? All the cool kids are using Coffeescript these days. – Aldo Bucchi Nov 24 '13 at 15:38
  • 3
    I'm guessing people don't see how using CoffeeScript answers the original question. Helpful as it may be, using CoffeeScript doesn't dictate organizing your code in a certain way. – Trevor Jan 24 '14 at 18:42