A Simple JavaScript Hooks System

August 19, 2010

I was looking to add some more extensibility to a project this week and I couldn’t find a hook system for JavaScript. I wanted something similar to the PHP hook system in MediaWiki, but Google just wasn’t much help.

I’m sure there is something out there that does what I need, but it’s such a simple thing I went ahead and implemented it.

hook.js

Extensions can “get in line” for a hook by calling register with the hook name and callback.

Core code (or even other extensions actually) can call hooks by using the call method, with name and an argument array (think argv). If a hook returns anything other than true, processing of the hook ceases.

To do useful things you have to set up the right arguments. Since objects are passed by reference in JavaScript, you can manipulate anything in the argument array from inside of your hook (or even add to the array if you want).

Obviously this is a simplified tool. All code is implicitly trusted, argument specification is non-existent, there is no prioritization (except for insertion order) and hooks are not guaranteed to run. But it works!

You can check out some basic usage code and test it out here.

Suggestions are welcome!

Comments

  1. Reed Hewitt says:

    Thanks for sharing! I’m working on a project right now that needs a simple hook architecture and this will work great. Like you said, there’s not a lot of useful information on Google about this kind of thing, so you saved me a lot of time.

    Cheers,
    Reed

  2. Sander says:

    Super, just started using this in my Meteor project!

  3. Jason Smith says:

    If you can use jQuery, the process couldn’t be simpler
    Register callback:

    Call hook

  4. Shubhojoy Mitra says:

    PrototypeJS has quite a few functions like bind, bindAsEventListener, curry, methodize, etc. On the other hand, ProtoypeJS also provides custom events which are pretty nice actually:

    $(el).fire("hook:widget", { name: type, status: true, data: obj });

    Call custom event/hook:

    $(el).on("hook:widget", function(evt){
    hookType = evt.memo.name;
    if(evt.memo.status)requiredData = evt.memo.data;
    /* do stuff */
    });

    (Note that evt has special property “memo” to pass various kinds of data)

  5. Example using prioritazion :)

    var Hook = {
    hooks: [],
    register: function(name, callback, priority) {
    if (typeof Hook.hooks[name] == "undefined") {
    Hook.hooks[name] = [];
    }
    if (parseInt(priority, 10) === priority) { // should be a valid integer
    if (Hook.hooks[name].length > priority + 1) {
    Hook.hooks[name].splice(priority, 0, callback);
    } else {
    Hook.hooks[name].push(callback);
    }
    } else {
    Hook.hooks[name].push(callback);
    }
    },
    call: function(name, arguments) {
    if (typeof Hook.hooks[name] != "undefined") {
    for (var i = 0, len = Hook.hooks[name].length; i < len; ++i) {
    if (Hook.hooks[name][i](arguments) !== true) break;
    }
    }
    }
    };

  6. Donut says:

    I’ve written a little CoffeeScript version of this that also returns the aggregated return values of the callbacks.

    https://gist.github.com/donut/f16040209cdc6f5e62c2

Leave A Comment

Your email will not be published.