Follow me: @D_mitar

Most read posts recently



Feb 18th 2011 × How to clone element storage in mootools

Ever needed to clone an element and take your element storage with it? I have and it’s not easy. There’s Element.cloneEvents(), which takes the storage.events callbacks but any other storage you may have used remains unavailable behind the closure that MooTools uses for the storage object.

This bit of code tracks of any data you store (via the lstore method only) in the element’s native storage and then provides a new Element prototype Element.cloneStorage(srcElement).

(function() {
    var eliminateOrig = Element.prototype.eliminate;
    var localStorage = {};

    var get = function(uid){
        return (localStorage[uid] || (localStorage[uid] = [])); // converted to an array to store keys stored
    };

    Element.implement({
        lstore: function(property, value) {
            // use this instead of Element.store
            var storage = get(this.uid);
            // store the key so we can clone it after
            if (!storage[property])
                storage.push(property);
            return this.store.apply(this, arguments);
        },
        eliminate: function(property) {
            var storage = get(this.uid);
            storage.erase(property);
            return eliminateOrig.apply(this, arguments);
        },
        cloneStorage: function(from) {
            // new prototype to clone
            var other = get(from.uid);
            other.each(function(el) {
                this.lstore(el, from.retrieve(el));
            }, this);
            return this;
        }
    });
})();

Here is an example usage test:

document.id("bar").lstore("hello", "there");
document.id("foo").lstore("foo", "bar");
document.id("foo").cloneStorage(document.id("bar"));

// if clone works this should now say 'there':
console.log(document.id("foo").retrieve("hello"));

document.id("foo").store("ugly", "face");
console.log(document.id("foo").retrieve("ugly"));

// testing eliminate.
document.id("bar").lstore("eliminateTest", new Image());
document.id("bar").eliminate("eliminateTest");
document.id("foo").cloneStorage(document.id("bar"));
//should now be null
console.log(document.id("foo").retrieve("eliminateTest"));

To view this on jsfiddle: http://jsfiddle.net/dimitar/225HJ/

And the gist: https://gist.github.com/833584

Thanks to Tim Weink (Timmeh aka FunFactor) for the inspiration and also for pointing out that copying ALL storage is not a good idea due to class instances being stuffed there for things like Fx.Tween / Morph / Request etc, hence making me refactor it to only clone the storage that was passed through the lstore method.


Mar 11th 2010 × porting element storage from mootools 1.2 to 1.11 and 1.1.2

This is a very nice feature, one of the great ideas that came with mootools 1.2. You can store any property within elements by simply mimicking the markup of <div myprop=”myvalue”> in javascript. However, setting such proprietary element properties in IE can be very slow. What mootools 1.2 did was to abstract element storage into a global object with the elements’ UID as key. Very smart, particularly as it allows for storing not only simple text assignments but even references to classes / instances, arrays and so forth.

Unfortunately, some of us (like the Joomla users) are stuck with using mootools 1.1.x for the time being and cannot take advantage of the element storage system. I am so used to keeping data contextual to elements within their storage that I ported the whole thing from 1.2. Here is all you need to achieve the .store and .retrieve element methods:

(function() {
    // compatibility layer with mootools 1.2 element storage system
    // scoped
    var storage = {}, Native = {
        UID: 1
    };

    var $uid = (window.ie) ? function(item){
        return (item.uid || (item.uid = [Native.UID++]))[0];
    } : function(item){
        return item.uid || (item.uid = Native.UID++);
    };

    var get = function(uid){
        return (storage[uid] || (storage[uid] = {}));
    };

    Element.extend({
        retrieve: function(property, dflt){
            if (!this.uid)
                $uid(this);

            var storage = get(this.uid), prop = storage[property];
            if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
            return $pick(prop);
        },
        store: function(property, value){
            if (!this.uid)
                $uid(this);

            var storage = get(this.uid);
            storage[property] = value;
            return this;
        }
    });
})();

To see this in action, play with my simplified jfiddle example below: