Quick Left

This is a blog

GIFs, tech and stuff.

MonkeyFist: an initialization micro-framework for jQuery


To kick off 2011, I decided to scratch a personal itch and wrote a
simple javascript application bootstrapper called MonkeyFist.
MonkeyFist came about because I was fed up with having to include a
document ready function in every file in a large application, which is
something I've always had misgivings about.

It starts like this: you're tasked with writing some basic javascript
in simple application. The javascript should cover a couple common areas
of frontend interaction, such as directing all outbound links to open
new tabs, setting text inputs to automattically clear default text, and
maybe some generic DOM manipulation (like showing and hiding elements in
a FAQ). This is all easy! You quickly whip out a script that covers all
these basics, and to sweeten the deal, you've even declared a custom
namespace for your application to promote code re-use and organization!

Your namespace contains an initialization function that you pass into
jQuery's document ready handler and sit back to admire a job well done.

After a while, you add on some additional features and requirements, and your
monolithic file becomes larger and large, with more and mroe javascript
getting loaded on each page. In an effort to regain a certain level of
sanity, you break the file up into page and functionality specific
sections. All of your ajax abstraction gets it own file, along with each
view that has custom scripting requirements. Since you were smart and
declared your own namespace, you're able to expand functionality in a
way that lets you share code between files.

But now you've got a bit of a problem.

Despite your best efforts, you now have document ready invoked multiple
times on each pageload. To make matters worse, since you're following
the rules and minifying & concatinating all of your javascript in
production
, so in reality you're calling document ready multiple
times in the same file!
Just think about it this way:

Would you ever do this in the same file?

$(document).ready(function(){
  $('a').click(function(){ ... });
});

$(document).ready(function(){
  $('.some_other_elem').slideDown(500);
});

NO, OF COURSE NOT!!!

What if there was a good way to define a core file as a mixin (an
OOP construct which javascript doesn't offer natively) to be
referenced in other files? Well that wouldn't be that difficult, but it
would require either setting up an inheretence structure for your
initialization functions (read: not a walk in the park), or you'd have
to require a call to your core initializer in every one of your files
(easy to forget). Neither option is particularly appealing when you set
out to write what seemed to be some pretty boilerplate code.

Enter the MonkeyFist

MonkeyFist solves these problems by providing default handlers for
pre-DOM ready and docuement ready in an extremely flexible way. Instead
of locking you into an order of execution, MonkeyFist allows for
"callback hoisting"--a way to specify if a specific callback is executed
before or in place of the default handler.

At its core, MonekyFist simply contructs a series of functions that are
executed in a specific order. The setup is very simple:

Just method override the default handlers in MonkeyFist using the
contructor function MF.helper.monkey() in your core file.

MF.liveEvents = MF.helper.monkey('preDom', function(){
  // your code goes here
});

MF.bindEvents = MF.helper.monkey('postDom', function(){
  // your code goes here
});

Those would contain an global javascript that you'd want to run on every
page. You can also add your code directly into monkeyfist.js if you
prefer.

To execute these functions, just call MF.initialize() without any
arguments.

Extending these defaults in another file is as easy as passing
functions into MF.initialize()

someOtherFile.js:

function myAwesomeLiveEvents(){
  // rad sick jqueries
}

function myAwesomeBindEvents(){
  // even moar rad sick jqueries
}

MF.initialize( myAwesomeLiveEvents, myAwesomeBindEvents );

Doing that will cause the following to happen:

  1. Call default liveEvents
  2. Call myAwesomeLiveEvents
  3. Wait for document ready
  4. Call default bindEvents
  5. Call myAwesomeBindEvents

Examples covering "callback hoisting" and other advanced uses can be found in the MonkeyFist
readme
.

A clear solution

MonkeyFist certainly isn't for everything. If you're looking for something that
provides a more complex interface to address this problem, look to
Backbone.js from DocumentCloud, which provides an awesome framework
for building rich, data-driven applications.

But, if you're interested in a very small (~1kb minified) solution for
applications that grow and exapnd over time, MonkeyFist probably has an
answer.