December 5, 2009 will

Javascript Snippets

Since I've been hacking away with Javascript lately, I thought I'd share a few snippets that I have found invaluable.

Some of these snippets use JQuery, but could easily be modified to use plain JS or another toolkit.

Some of these snippets are likely available in other libraries and plugins, but I prefer not to introduce too many dependencies when working with Javascript. Better to keep the code lean – if possible!

Simple template substitution

Building strings with Javascript can be a pain if it consists of a number of parts, and it isn't too flexible if you want to modify later. Here's a snippet that adds simple template substitution to strings.

String.prototype.f = function(data){
    var template = this.toString();
    return template.replace( /{{\s*(.*?)\s*}}/g, function(m, n) {
        var node = data;
        $.each(n.split('.'), function(i, symbol){
            node = node[symbol];
        });
        return node;
    });
}

It adds a method, f, which takes an object and replaces parts of the string between double braces with values from that object, similar to Django templates. Here's an example:

var html = '<p class="{{ class }}">Hello, {{ name }}!</p>'.f({name:"World", class:"example"});

Which produces:

<p class="example">Hello, World!</p>

I've found this has satisfied my template needs in locidesktop, but I may flesh it out at some point with conditionals and loops to make it more like a full template language.

Preloading images

When you replace an image with Javascript, you get an unpleasant visual glitch before the browser has downloaded the new image. You either see a broken image square if it is an img tag, or no image at all in the case of CSS background images.

It is better to delay the image replacement until the browser has retrieved the new image. It turns out that it is pretty simple to implement such a system. The following snippet takes an image url and a callback. The callback is invoked when the browser has started downloading the image, and it is this callback that should insert the image in to the DOM.

function dynamic_preload(img_url, callback)
{
    preload_id += 1;
    $('#dynamic-preload').append('<img id="dynamic-preload-id-{{ preload_id }}" src="{{ img_url }}"/>'.f({img_url:img_url, preload_id:preload_id}));
    var $preload_img = $('#dynamic-preload-id-{{ preload_id }}'.f({preload_id:preload_id}));
    function check_preload()
    {
        if ($preload_img.width()) {
            if (callback) {
                callback();
            }
            $preload_img.remove();
        } else {
            setTimeout(check_preload, 100);
        }
    }
    check_preload();
}

This snippet requires the presence of the following piece of html, which creates an invisible element to put the preloading images.

<div id="dynamic-preload" style="width:0px;height:0px;overflow:hidden;">

Interestingly, display:none; for this element will break the pre-loading because the browser wont start loading any images it thinks are truly invisible; you have to trick it by setting the dimensions to zero by zero pixels and hiding any overflow.

The preloading works by inserting an image tag in to the DOM, and monitoring the IMG element until it has a width greater than zero – which indicates the browser has at least retrieved the image header.

It doesn't guarantee that the entire image has loaded, however, but it will generally eliminate the visual glitches involved with replacing images.

If anyone knows how to detect that an image has loaded in its entirety, let me know!

Javascript classes with inheritance

Javascript doesn't have classes, as such. But you can create a pretty close approximation with this snippet:

function Class(definition)
{
    var prototype = definition;

    if (definition['_extends'] !== undefined)
    {
        $.each(definition['_extends'].prototype, function(k, v){

            if (prototype[k] === undefined) {
                prototype[k] = v;
            } else {
                prototype[k+'_super'] = v;
            }
        });
    }

    var constructor = definition['_init'];
    if (constructor === undefined)
    {
        constructor = function()
        {
            if (this.prototype('_init_super')) {
                this._init_super();
            }
        }
    }
    constructor.prototype = prototype;
    return constructor
}

This allows you to create classes in a fashion similar to fully Object Oriented languages. You call the Class function with a dictionary of functions which become methods for the class.

You can also provide a _extends value which should be an existing classs. The new class will contain all the methods from this base class plus any new methods it defines itself. Methods in the derived class can access methods from the base class by appending _super to the method name.

The method _init has special meaning, and is the constructor of the class.

This is best explained with a little example:

A = new Class({
    _init:function(m)
    {
        this.m = m;
    },
    say:function()
    {
        console.log(this.m + " from A");
    }
});

a = A("hello");
a.say();

This create a class object, A, and an instance a. Calling the say method will output “hello from A” to the logs (if you have Firebug).

We can build on the A class by defining a class, B that extends from A. Instances of b will have access to their own methods, and the methods of A:

B = new Class({
    _extends:A,
    _init:function(m)
    {
        this._init_super(m);
    },
    say:function()
    {
        console.log(this.m + " from B");
        this.say_super();
    }
});

b = B("hai");
b.say();

This will output “hai from B” and “hai from A” to the console.

Again, this could be enhanced to be a more fully features class system, but I have found this snippet satisfies my requirements for Python-like classes!

I'm no expert in Javascript, if there are better ways of doing things than these snippets – do post them!

Use Markdown for formatting
*Italic* **Bold** `inline code` Links to [Google](http://www.google.com) > This is a quote > ```python import this ```
your comment will be previewed here
gravatar
shaman.sir
This method breaks if there is C that extends B and also calls _init_super from its _init method - it comes into recursion because there are two “_init_super” methods are in prototype of “this”.

I've used your example, it is nice in the end, and I hoped it will work for me, but there is a mis-design :(