views:

80

answers:

5

I would like to get some advice on structuring my javascript code and jquery functions. I like to use JQuery for dom event handling and ajax etc. I have written what seems like poor code in the past where my entire js file consists of a bunch of anonymous Jquery functions.

I am wondering - what does a "well structured" javascript file look like when combining with JQuery? Will there be many standard javascript functions and then some JQuery $() functions put in when needed? Is it ok to treat the code in a purely procedural way or would it be best to use some OOP concepts? I have always just had a bunch of functions which call each other hierarchically with helper functions here and there and no objects (aside from those used when using some random 3rd party library).

I am not a javascript programmer (mainly Java) so any advice from experienced js developers would be great.

+1  A: 

i suggest object-oriented structure, not only, because the concept is state of the art, but also for overview reason you should encapsulate functions which belong to each other.

if you have a large js file (you should have only one, for performance reason) you don't find anything after some weeks if you don't structure your code by functionality.

helle
"you should have only one, for performance reason" ... or better split your code up into separate files, and use a custom handler to combine and load it all as one request.
Chad
Honestly I find that Javascript, especially with jQuery, doesn't really work too well as an OOP environment. jQuery itself really isn't an OO library.
Pointy
+2  A: 

Honestly, I try to apply MVC concepts to jQuery. Once you get over about 500 lines, not doing so just makes it hard to keep up with stuff.

Model

  • Any locally cached data
  • Cookies
  • Reading-from/writing-to the server (AJAX)

View

  • Basically all your DHTML stuff (HTML generation/manipulation, CSS changes, animations/effects, etc)

Controller

  • Event handling (both user events and custom events)

There are even some projects out there for this.

Peter Bailey
+1  A: 

If your get in bed with jQuery, then my suggestion would be to get all the way in bed. That means writing your own jQuery plugin code when appropriate, which would be whenever you find your self wanting to write a function that takes a jQuery object as a parameter.

Because the nature of Javascript code for web pages is such that essentially everything happens in event loops, most routines are pretty small. Probably the biggest chunk of work to be done is the code that initializes all the handlers. When you're putting together an application website, with many pages, you have a choice to make:

  • Let every page be its own special snowflake, with its own Javascript for its own behaviors

or

  • Collect and standardize behaviors for all your pages into a single global mechanism

I've lived through both, and the second way is definitely better but it's got its own problems. You have to stay on top of things and make sure that the special cases don't result in code bleeding into your HTML (or JSP) sources. Somebody has to deal with the performance issues of site-global script code trying to apply itself to every page. And, of course, you start to grow a monstrous initialization function.

What saves me from maintenance hell on something like that is a build step that combines separate script files (one per page "feature", more or less, plus custom jQuery plugins) into a single monolithic script to actually deploy to the server. I did that with FreeMarker but there are lots of other ways to do it. A nice by-product of having such a step is that it's a good time to run YUICompressor.

Pointy
A: 

Firstly, you can use a Object Style Approach, Methods over Functions

Example

var User = {
    loggedin : false,
    name : "Robert PItt",
    id : 45
}

And you can create a System Item within js file like so

if(typeof System == undefined)
{
    System = new Object();
}

System.Globals = 
{
   //Some Global Settings,uri's etc in here
}

System.Gui = {
   Init : function()
   {
      if(User.loggedin == true)
      {
          this.RemoveItem("#userlogin");
      }
   },
   RemoveItem: function(form)
   {
       $(form).remove();
   }
}

System.Gui.init(); //Within head maybe/

As you can see you can create several "containers" to hold your methods within, containers can be helpful to organise your code and separate it at the same time, you can create along side the GUI things such as Ajax,Tools,Security,Callbacks and so on.

RobertPitt
+1  A: 

I have to second Peter Bailey's answer in that it's best to keep your M V and C separate. I found the best style for GUI widgets was to create a function which initialized the widget (creating HTML, adding style, or attaching animation events), then attaching custom event handlers for the different places where it is used.

Custom events are key for reusable widgets. It make code less brittle to DOM changes in your GUI widget and makes your code more readable.

For example, let's say you have a custom button which dynamically creates an HTML structure like so:

<div class="MyButton"><div class="button-helpful-wrapper"><img src="/blah.png"/></div></div>

Let's say your initialization code adds some fades or whatever using click events. It doesn't matter.

The interesting bit is when you want to use this button; to which div do you attach the 'click' event to? instead of having to know the structure of your GUI widget, it would be better to simply create a new type of event for the widget. Like so:

$('#MyButton_Instance1').bind('myclick', function () {...} );

And be completely agnostic about the structure of the widget.

To make this work all you have to do is add some binds/triggers which proxy the actual event to your custom event. The best place to put this is in your widget initialization code:

$('.MyButton').myButton();

Where

myButton = function () {
    $(this).find('.button-helpful-wrapper').bind('click', function (event) {
        $(this).trigger('myclick', event);
    });
}

This makes your widgets very reusable because you're not attaching to specific DOM element events but rather a generic widget event. Try it.

brendan