views:

9041

answers:

23

As JavaScript frameworks like jQuery make client side web applications richer and more functional, I've started to notice one problem...

How in the world do you keep this organized?

  • Put all your handlers in one spot and write functions for all the events?
  • Create function/classes to wrap all your functionality?
  • Write like crazy and just hope it works out for the best?
  • Give up and get a new career?

I mention jQuery, but it's really any JavaScript code in general. I'm finding that as lines upon lines begin to pile up, it gets harder to manage the script files or find what you are looking for. Quite possibly the biggest propblems I've found is there are so many ways to do the same thing, it's hard to know which one is best.

Are there any general recommendations on the best way to keep your .js files as nice and neat as the rest of your application? Or is this just a matter of IDE? Is there a better option out there?


EDIT

This question was intended to be more about code organization and not file organization. There has been some really good examples of merging files or splitting content around.

My question is: what is the best way to organize your actual code? What is your way, or even a recommended way to interact with page elements and create reuseable code that doesn't conflict with each other?

Some people have listed namespaces which is a good idea. What are some other ways, more specifically dealing with elements on the page and keeping the code organized and neat?

+22  A: 

You can break up your scripts into separate files for development, then create a "release" version where you cram them all together and run YUI Compressor or something similar on it.

Greg
Sometimes there are unneeded javascript scripts. It's wasteful to send those to the client. I think it's bes to only send what is needed. Of course, for a web app that's being used all day long, such as an intranet app, it might be better to send the whole batch at once, on the first page load.
DOK
+3  A: 

A few days ago, the guys at 37Signals released a RTE control, with a twist. They made a library that bundles javascript files using a sort of pre-processor commands.

I've been using it since to separate my JS files and then in the end merge them as one. That way I can separate concerns and, in the end, have only one file that goes through the pipe (gzipped, no less).

In your templates, check if you're in development mode, and include the separate files, and if in production, include the final one (which you'll have to "build" yourself).

changelog
http://getsprockets.org/ is the direct link
Matt Gardner
A: 

I use a custom script inspired by Ben Nolan's behaviour (I can't find a current link to this anymore, sadly) to store most of my event handlers. These event handlers are triggered by the elements className or Id, for example. Example:

Behaviour.register({ 
    'a.delete-post': function(element) {
        element.observe('click', function(event) { ... });
    },

    'a.anotherlink': function(element) {
        element.observe('click', function(event) { ... });
    }

});

I like to include most of my Javascript libraries on the fly, except the ones that contain global behaviour. I use Zend Framework's headScript() placeholder helper for this, but you can also use javascript to load other scripts on the fly with Ajile for example.

Aron Rotteveel
Is this what you were looking for? http://www.koders.com/javascript/fidEBB758A268EBDAB42C72AAA954AA9A3099A1DE67.aspx?s=%22Ben+Nolan%22#L4
DOK
Yup, that's the one! :) Seems like the code behind the link is quite newer than the version I was inspired by, though. Thanks for your effort!
Aron Rotteveel
+5  A: 

My boss still speaks of the times when they wrote modular code (C language), and complains about how crappy the code is nowadays! It is said that programmers can write assembly in any framework. There is always a strategy to overcome code organisation. The basic problem is with guys who treat java script as a toy and never try to learn it.

In my case, I write js files on a UI theme or application screen basis, with a proper init_screen(). Using proper id naming convention, I make sure that there are no name space conflicts at the root element level. In the unobstrusive window.load(), I tie the things up based on the top level id.

I strictly use java script closures and patterns to hide all private methods. After doing this, never faced a problem of conflicting properties/function definitions/variable definitions. However, when working with a team it is often difficult to enforce the same rigour.

questzen
+35  A: 

It would be a lot nicer if javascript had namespaces built in, but I find that organizing things like Dustin Diaz describes here helps me a lot.

var DED = (function() {

    var private_var;

    function private_method()
    {
        // do stuff here
    }

    return {
        method_1 : function()
            {
                // do stuff here
            },
        method_2 : function()
            {
                // do stuff here
            }
    };
})();

I put different "namespaces" and sometimes individual classes in separate files. Usually I start with one file and as a class or namespace gets big enough to warrant it, I separate it out into its own file. Using a tool to combine all you files for production is an excellent idea as well.

polarbear
yup that was my AHHHH experience with JavaScript
zedoo
I usually refer to that as "the crockford way". +1 from me
Matt Briggs
You can even go a bit further. See this link: http://www.wait-till-i.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/
MKroehnert
+4  A: 

I create singletons for every thing I really do not need to instantiate several times on screen, a classes for everything else. And all of them are put in the same namespace in the same file. Everything is commented, and designed with UML , state diagrams. The javascript code is clear of html so no inline javascript and I tend to use jquery to minimize cross browser issues.

Nikola Stjelja
matt lohkamp
A: 

You don't mention what your server-side language is. Or, more pertinently, what framework you are using -- if any -- on the server-side.

IME, I organise things on the server-side and let it all shake out onto the web page. The framework is given the task of organising not only JS that every page has to load, but also JS fragments that work with generated markup. Such fragments you don't usually want emitted more than once - which is why they are abstracted into the framework for that code to look after that problem. :-)

For end-pages that have to emit their own JS, I usually find that there is a logical structure in the generated markup. Such localised JS can often be assembled at the start and/or end of such a structure.

Note that none of this absolves you from writing efficient JavaScript! :-)

staticsan
+10  A: 

Inspired by earlier posts I made a copy of Rakefile and vendor directories distributed with WysiHat (a RTE mentioned by changelog) and made a few modifications to include code-checking with JSLint and minification with YUI Compressor.

The idea is to use Sprockets (from WysiHat) to merge multiple JavaScripts into one file, check syntax of the merged file with JSLint and minify it with YUI Compressor before distribution.

Prerequisites

  • Java Runtime
  • ruby and rake gem
  • You should know how to put a JAR into Classpath

Now do

  1. Download Rhino and put the JAR ("js.jar") to your classpath
  2. Download YUI Compressor and put the JAR (build/yuicompressor-xyz.jar) to your classpath
  3. Download WysiHat and copy "vendor" directory to the root of your JavaScript project
  4. Download JSLint for Rhino and put it inside the "vendor" directory

Now create a file named "Rakefile" in the root directory of the JavaScript project and add the following content to it:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

If you done everything correctly, you should be able to use the following commands in your console:

  • rake merge -- to merge different JavaScript files into one
  • rake check -- to check the syntax of your code (this is the default task, so you can simply type rake)
  • rake minify -- to prepare minified version of your JS code

On source merging

Using Sprockets, the JavaScript pre-processor you can include (or require) other JavaScript files. Use the following syntax to include other scripts from the initial file (named "main.js", but you can change that in the Rakefile):

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

And then...

Take a look at Rakefile provided with WysiHat to set the automated unit testing up. Nice stuff :)

And now for the answer

This does not answer the original question very well. I know and I'm sorry about that, but I've posted it here because I hope it may be useful to someone else to organize their mess.

My approach to the problem is to do as much object-oriented modelling I can and separate implementations into different files. Then the handlers should be as short as possible. The example with List singleton is also nice one.

And namespaces... well they can be imitated by deeper object structure.

if (typeof org !== 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

I'm not big fan of imitations, but this can be helpful if you have many objects that you would like to move out of the global scope.

Damir Zekić
+7  A: 

I was able to successfully apply the Javascript Module Pattern to an Ext JS application at my previous job. It provided a simple way to create nicely encapsulated code.

Alan
+27  A: 

I try to avoid including any javascript with the HTML. All the code is encapsulated into classes and each class is in its own file. For development, I have separate <script> tags to include each js file, but they get merged into a single larger package for production to reduce the overhead of the HTTP requests.

Typically, I'll have a single 'main' js file for each application. So, if I was writing a "survey" application, i would have a js file called "survey.js". This would contain the entry point into the jQuery code. I create jQuery references during instantiation and then pass them into my objects as parameters. This means that the javascript classes are 'pure' and don't contain any references to CSS ids or classnames.

// file: survey.js
$(document).ready(function() {
  var jS = $('#surveycontainer');
  var jB = $('#dimscreencontainer');
  var d = new DimScreen({container: jB});
  var s = new Survey({container: jS, DimScreen: d});
  s.show();
});

I also find naming convention to be important for readability. For example: I prepend 'j' to all jQuery instances.

In the above example, there is a class called DimScreen. (Assume this dims the screen and pops up an alert box.) It needs a div element that it can enlarge to cover the screen, and then add an alert box, so I pass in a jQuery object. jQuery has a plug-in concept, but it seemed limiting (e.g. instances are not persistent and cannot be accessed) with no real upside. So the DimScreen class would be a standard javascript class that just happens to use jQuery.

// file: dimscreen.js
function DimScreen(opts) { 
   this.jB = opts.container;
   // ...
}; // need the semi-colon for minimizing!


DimScreen.prototype.draw = function(msg) {
  var me = this;
  me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
  //...
};

I've built some fairly complex appliations using this approach.

Jason Moore
+5  A: 

Following good OO design principals and design patterns goes a long way to making your code easy to maintain and understand. But one of the best things I've discovered recently are signals and slots aka publish/subscribe. Have a look at http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html for a simple jQuery implementation.

The idea is well used in other languages for GUI development. When something significant happens somewhere in your code you publish a global synthetic event which other methods in other objects may subscribe to. This gives excellent separation of objects.

I think Dojo (and Prototype?) have a built in version of this technique.

see also http://stackoverflow.com/questions/312895/signals-and-slots

meouw
+4  A: 
Eugene Lazutkin
A: 

Lazy Load the code you need on demand. Google does something like this with their google.loader

+1  A: 

In my last project -Viajeros.com- I've used a combination of several techniques. I wouldn't know how to organize a web app -- Viajeros is a social networking site for travellers with well-defined sections, so it's kind of easy to separate the code for each area.

I use namespace simulation and lazy loading of modules according to the site section. On each page load I declare a "vjr" object, and always load a set of common functions to it (vjr.base.js). Then each HTML page decides which modules need with a simple:

vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];

Vjr.base.js gets each one gzipped from the server and executes them.

vjr.include(vjr.Required);
vjr.include = function(moduleList) {
  if (!moduleList) return false;
  for (var i = 0; i < moduleList.length; i++) {
    if (moduleList[i]) {
      $.ajax({
        type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
      });
    }
  }
};

Every "module" has this structure:

vjr.comments = {}

vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }

// Handlers
vjr.comments.setUpUI = function() {
    // Assign handlers to screen elements
}

vjr.comments.init = function () {
  // initialize stuff
    vjr.comments.setUpUI();
}

$(document).ready(vjr.comments.init);

Given my limited Javascript knowledge, I know there must be better ways to manage this, but until now it's working great for us.

Danita
+3  A: 

"Write like crazy and just hope it works out for the best?", I've seen a project like this which was developed and maintained by just 2 developers, a huge application with lots of javascript code. On top of that there were different shortcuts for every possible jquery function you can think of. I suggested they organize the code as plugins, as that is the jquery equivalent of class, module, namespace... and the whole universe. But things got much worse, now they started writing plugins replacing every combination of 3 lines of code used in the project. Personaly I think jQuery is the devil and it shouldn't be used on projects with lots of javascript because it encourages you to be lazy and not think of organizing code in any way. I'd rather read 100 lines of javascript than one line with 40 chained jQuery functions (I'm not kidding). Contrary to popular belief it's very easy to organize javascript code in equivalents to namespaces and classes. That's what YUI and Dojo do. You can easily roll your own if you like. I find YUI's approach much better and efficient. But you usualy need a nice editor with support for snippets to compensate for YUI naming conventions if you want to write anything useful.

Vasil
I'd agree with you about really long, chained commands, but one of the best parts of jQuery is that it keeps all Javascript out of HTML. You can setup event handlers for all your elements without "needing" to add IDs or on<whatever> events on your elements. As always, over use of any tool is bad...
Hugoware
A: 

To Vasil: I think your developers have an organization problems. We write applications with pages that exceed 1000 lines of javascript code and there's never been a problem with any of them (expanding or debugging). Organizing your code (in any language) is a skill you only gain with experience.

jdsushi
A: 

I think this ties into, perhaps, DDD (Domain-Driven Design). The application I'm working on, although lacking a formal API, does give hints of such by way of the server-side code (class/file names, etc). Armed with that, I created a top-level object as a container for the entire problem domain; then, I added namespaces in where needed:

var App;
(function()
{
    App = new Domain( 'test' );

    function Domain( id )
    {
     this.id = id;
     this.echo = function echo( s )
     {
      alert( s );
     }
     return this;
    }
})();

// separate file
(function(Domain)
{
    Domain.Console = new Console();

    function Console()
    {
     this.Log = function Log( s )
     {
      console.log( s );
     }
     return this;
    }
})(App);

// implementation
App.Console.Log('foo');
ken
+4  A: 

Organising your code in a Jquery centric NameSpace way may look as follows... and will not clash with other Javascript API's like Prototype, Ext either.

<script src="jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script type="text/javascript">

var AcmeJQ = jQuery.noConflict(true);
var Acme = {fn: function(){}};

(function($){

    Acme.sayHi = function()
    {
     console.log('Hello');
    };

    Acme.sayBye = function()
    {
     console.log('Good Bye');
    };
})(AcmeJQ);

// Usage
//    Acme.sayHi();
// or
// <a href="#" onclick="Acme.sayHi();">Say Hello</a>


</script>

Hope this helps.

Darryl
+2  A: 

Create fake classes, and make sure that anything that can be thrown into a separate function that makes sense is done so. Also make sure to comment a lot, and not to write spagghetti code, rather keeping it all in sections. For example, some nonsense code depicting my ideals. Obviously in real life I also write many libraries that basically encompass their functionality.

$(function(){
    //Preload header images
    $('a.rollover').preload();

    //Create new datagrid
    var dGrid = datagrid.init({width: 5, url: 'datalist.txt', style: 'aero'});
});

var datagrid = {
    init: function(w, url, style){
        //Rendering code goes here for style / width
        //code etc

        //Fetch data in
        $.get(url, {}, function(data){
            data = data.split('\n');
            for(var i=0; i < data.length; i++){
                //fetching data
            }
        })
    },
    refresh: function(deep){
        //more functions etc.
    }
};
Dmitri Farkov
+2  A: 

I use Dojo's package management (dojo.require and dojo.provide) and class system (dojo.declare which also allows for simple multiple inheritance) to modularize all of my classes/widgets into separate files. Not only dose this keep your code organized, but it also lets you do lazy/just in time loading of classes/widgets.

Justin Johnson
A: 

A quick note to mention there are some great posts being made here, going to print it all up and look through it - thanks to all!!

Quinton.

+3  A: 

Check out JavasciptMVC.

You can :

  • split up your code into model, view and controller layers.

  • compress all code into a single production file

  • auto-generate code

  • create and run unit tests

  • and lots more...

Best of all, it uses jQuery, so you can take advantage of other jQuery plugins too.

andyuk
Yup, I've used jmvc and it's pretty good - docs could be better though
meouw
A: 

For JavaScript organization been using the following

  1. Folder for all your javascript
  2. Page level javascript gets its' own file with the same name of the page. ProductDetail.aspx would be ProductDetail.js
  3. Inside the javascript folder for library files I have a lib folder
  4. Put related library functions in a lib folder that you want to use throughout your application.
  5. Ajax is the only javascript that I move outside of the javascript folder and gets it's own folder. Then I add two sub folders client and server
  6. Client folder gets all the .js files while server folder gets all the server side files.
Jason Too Cool Webs
Nice for file organization. I do that with code. But in the end I compile my code in a... let's say dll. You need that with javascript too or you'll end up requesting 15 js files per page.
graffic