Prototype vs jQuery

by Naneau

One of prototype’s often overlooked functions is Element.addMethods(). Especially in these days of comparison between jQuery and Prototype, it’s important. The basic idea behind jQuery is that you can do a CSS selector query, and apply methods (even chaining them) to the result. Like:

1
2
$('#element').after('I get inserted after #element');
//insert some html after an element

In prototype, you could get the same result by doing:

1
2
new Insertion.After('element', 'I get inserted after #element');
//it's slightly longer! oh noez!

Prototype encapsulates it’s methods in classes. I personally like this way of working, because it keeps things separated and easy to read up on. There are of course people who like jQuery’s approach better because it keeps everything in one place, and that’s fine. They are both solid frameworks. I have read articles on how jQuery would be better than prototype, and vice versa, but in the end they both provide pretty much the same functionality.

Now, back to Element.addMethods(). Prototype allows you to add methods to it’s extended elements. Those are the kind of elements you get from the $() and $$() functions, so even if you’re not sure what they are, you’re probably already using them. You could easily make prototype behave like jQuery by doing:

1
2
3
4
5
6
7
Element.addMethods({
    after: function(element, html) {
        element = $(element);
        new Insertion.After(element, html);
        return element;
    }
});

As you can see, the return element; bit ensures that you can easily chain it, so you can do:

1
$('element').after('abcdefg').after('hijklmnop!');

You could use this behavior to create really useful things. Let’s assume that you want a spinner image displayed next to an arbitrary element. For instance, after a user presses a button or link, you want your spinner displayed next to that. Giving feedback to users that something is happening is important, and this may be a clear way. Let’s assume you have a spinner image at ‘http://yoururl/spinner.gif’.

Prototype has a very handy class called Position. With it you can clone the position of an element to another element. Even if the first element wasn’t really positioned in any way. We’re going to use that to make a spinner appear right next to the source element.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Element.addMethods({
    spin: function(element, text) {
        element = $(element);

        var img = '<img src="http://yoururl/spinner.gif" class="icon" alt="Hang on..." /> ';
        //change this to some url that has your spinner image

        if (!text) {
            text = '';
        }
        //make sure there's text in string form

        element.update('<div class="spin">' + img + text + '</div>')
        //update the element with a div.spin and the img

        return element;
        //return the element for chaining
    },

    stopSpin: function(element) {
        element = $(element);
        if (element) {
            //element exists
            element.innerHTML = '';
            //reset it's contents to nothing
        }
    },

    spinBeside: function (element, text) {
        element = $(element);
        //the element next to which we will spin

        id = (element.id);
        id = 'spin_' + id;
        //id for the spinner

        var html = '<div id="' + id + '"></div>';
        //spinner html

        var body = $$('body')[0];
        //body

        new Insertion.Bottom(body, html);
        //insert

        var spinElement = $(id);
        //the spinner element

        spinElement.spin(text);
        //make it spin, see the spin() method above

        Position.absolutize(spinElement);
        Position.clone(element, spinElement, {
            offsetLeft: element.getWidth() + 10,
            offsetTop: Math.round(element.getHeight() / 2 - spinElement.getHeight() / 2),
            setWidth: false,
            setHeight: false
        });
        //position it

        return element;
        //return element for chaining
    },

    stopSpinBeside: function(element, id) {
        element = $(element);
        //the element next to which there's a spinner

        id = (element.id);
        id = 'spin_' + id;
        //id for the spinner

        if ($(id)) {
            //check whether it exists to be sure
            $(id).remove();
            //remove the element from the document
        }

        return element;
        //return element for chaining
    }

});

You could use it on any element (as long as it has an id, but it wouldn’t be too hard to write around that), and get a nice little spinner next to it. Nifty, eh? With the addMethods() method you can add as many functions to Element as you wish. Prototype will automagically add them to every element it returns.