Follow me: @D_mitar

Most read posts recently



Oct 18th 2011 × Working with dynamic Class names in MooTools

This is somewhat trivial but is also useful and is a common question that a lot of people ask. The problem is in 2 parts: How to instantiate a Class based upon a dynamic name/variable and how to identify the Class name that has created a MooTools Class instance (the prototype, really).

First of all, to make a dynamic instance based upon a variable name, you simply need to know the name and context of execution (scope) of the Class you want to instantiate.

// define the class in the current scope / global object
this.foo = new Class();

// define the class in a namespace
var namespace = {
    foo: new Class()
};

// get the variable class name we need to use from any source into a variable...
var dynamic = 'foo';

// make the instances
var instance1 = new this[dynamic]();
var instance2 = new namespace[dynamic]();

So far, so good. This is pretty standard javascript and is self explanatory.

The harder bit comes when you have an instance and you are trying to find what Class name it is an instance of. We will create a small function that can help:

function getClassNameOfInstance(mootoolsClassInstance, context) {
    // query the context (this or custom object) for the instance we are working with
    return Object.keyOf(context || this, mootoolsClassInstance.constructor);
}

// use it on the global object
console.log("instance1 is: ", getClassNameOfInstance(instance1)); // foo

// use it on the namespace object
console.log("instance2 is: ", getClassNameOfInstance(instance2, namespace)); // foo

This function will check the context object for the constructor of the instance and return the ‘key’ if it matches, which will be MooTools Class name.

I hope it helps someone out there. Check it on JSFIDDLE: http://jsfiddle.net/dimitar/S4rR6/2/


Aug 24th 2011 × So long and thanks for all the fish

This is one of these posts that you write with your heart sunk and a feeling of regret… After 4+ fabulous years with WebTogs, I am moving on to pastures new.

It has been an absolute blast. I got the chance to start with a simple whiteboard idea and built a e-commerce start-up from the ground-up. A chance that I dare say took and did well out of, considering the lack of resources and other developers. Today, WebTogs is no longer a mere start-up site: it is a widely recognised and award-winning brand name, probably one of the biggest in the outdoors e-commerce industry. I cannot help but step back and marvel at our accomplishment with pride.

I got the chance to work with all aspects of the development process, from server administration, mysql structure and optimisation, PHP code, HTML 5, CSS3, javascript, SEO and SEM. I also got to discover the wonderful world of MooTools and have not looked back since.

But it is now time to pursue a career in what I have discovered that I enjoy doing the most: Front-End development with JavaScript and MooTools in particular. To that end, I am happy to say I have joined a new startup by QMetric and am looking forward to whatever new challenges that brings as well as whatever new skills and practices working there will allow me to develop.

I will continue helping out the guys at WebTogs as much as I can and hope we can continue our relationship together. Some of the people at Webtogs I have known and worked with for over 10 years and that’s a loooong time indeed. Thank you for all the great moments and the incredible mutual respect, flexibility and understanding that allowed us to create the site and build the business.

no

Jul 6th 2011 × Lazyloading multiple sequential javascript dependencies in MooTools

We all know and love Asset.js from MooTools-more. It affords you lazyloading by providing Asset.javascript, Asset.image, Asset.images and Asset.css. However, sometimes you need more precision when bringing in external scripts as dependencies.

Hence I created my own Asset.javascripts – based losely on a coupling of Asset.images and Asset.javascript but providing sequential asynchronous loading.

Asset.javascripts = function(sources, options) {
    // load an array of js dependencies and fire events as it walks through
    options = Object.merge({
        onComplete: Function.from,
        onProgress: Function.from
    }, options);
    var counter = 0, todo = sources.length;

    var loadNext = function() {
        if (sources[0])
            source = sources[0];

        Asset.javascript(source, {
            onload: function() {
                counter++;
                options.onProgress.call(this, counter, source);
                sources.erase(source);

                if (counter == todo)
                    options.onComplete.call(this, counter);
                else
                    loadNext();
            }
        });
    };

    loadNext();
};

Eg. usage with Arian’s DatePicker class, which uses multiple files that need to be loaded in their right order:

if (!window.Picker) {
    new Asset.javascripts([
        "/js/mylibs/Locale.en-US.DatePicker.js",
        "/js/mylibs/Picker.js",
        "/js/mylibs/Picker.Attach.js",
        "/js/mylibs/Picker.Date.js"
    ], {
        onComplete: function() {
            new Asset.css("/js/mylibs/datepicker.css");
            new Asset.css("/js/mylibs/datepicker_dashboard/datepicker_dashboard.css");

            doDates();
        }
    });
}
else {
    doDates();
}

Have fun.


Jul 6th 2011 × MooTools pattern fun: Class Implements + Extends at the same time

Update: Thanks to @CarstenSchwede, who pointed out that the MooTools Class Constructor expects the Extends mutator __before__ the Implements one, meaning you don’t need to worry about any of the stuff I had to come up with (described in this post). Upon further investigation with Arian, it turns out that MooTools 2.0 will NOT be dependent on order of Extends and Implements declarations in classes, thanks to this change by Kamicane.

The neverending debacle of Extends vs Implements, which one to use and how to use them together – has been a source of discomfort for me in MooTools for a while so I thought I’d write up parts of my experiences with it in the hope it may help somebody else.

I came to have a need to write a class that serves as my new mixin and brings a new method ‘kill‘ – (say, Class ninja) to another class (Class badass) that extends Class human. Pretty crazy, right?

The problem is, the pattern of:

var ninja = new Class({
    kill: function() {
        alert("kill!");
    }
});

var human = new Class({
    initialize: function(){
        alert("i r human!");
    }
});

var badass = new Class({
    Implements: [ninja],
    Extends: human,
    initialize: function() {
        alert("i r badass and.. ");
        this.parent();
        this.kill();
    }
});

new badass(); // i r badass, i r human, this.kill is not a function exception.

… simply does not work. The Extend here means the human prototype is the base and it lacks the kill method. Wut?

The main practical difference in subclassing approaches is that the `Implements` (mixin) does not instantiate the subclass whereas `Extends` does, so there can be only one class that is extended whereas implemented ones just copy the methods from the prototypes. How to get around being able to extend a single class only? You need class human to implement ninja instead and class badass to simply extend human. Aside from the side-effect of humans getting a new kill method (which they may or may not know about), it will mean that badass will be able to use .kill as well as call upon his direct parent human.

The whole point is, you may not always be at liberty to rewrite / refactor classes the way you want them without creating complications with other parts of your web app. You could also be be extending a Mootools native class like Request.JSONP and then decide to mixin a new storage class into your extended one (true story!)…

An interesting pattern (or rather, the only pattern I have found to work… ) to overcome this goes something like this:

var ninja = new Class({
    kill: function() {
        alert("kill!");
    }
})

var human = new Class({
    initialize: function(){
        alert("i r human!");
    }
});

human.implement(new ninja);

var badass = new Class({
    Extends: human,
    initialize: function() {
        alert("i r badass and.. ");
        this.parent();
        this.kill();
    }
});

new badass(); // this.kill works.

You could even write this as:

Extends: human.implement(new ninja),

Obviously, you can simply modiy the prototype of the parent class and do:

human.implement({
    kill: function() {
        alert("kills!");
    }
});

But this means the code you already have in ninja needs to be duplicated and this will go for all methods or properties you want to gain access to.

A practical example with Request.JSONP and an example caching class, created to allow use of localStorage. Why would you want to do that? Well, having a class that can allow you to cache replies from Request or Request.JSONP or anything at all is handy. And doing things that fetch non-volatile data through a rate-limited API is expensive, you are encouraged to cache. In other words, if you read a particular user’s Twitter profile / timeline, you should not have to request it again on a reload within the session. Same applies if you are expanding tiny URLs like bit.ly – the URL won’t change for any given hash so there’s no point in continuously fetching it.

In the following example, we cache the response for a user profile JSONP request via the Twitter API:

View the full fiddle here: http://jsfiddle.net/dimitar/AjP5A/.

For a real detailed explanation of Extends vs Implements in MooTools, grab a copy of Pro Javascript with MooTools by keeto.


May 13th 2011 × mootools flickr api class via Request.JSONP

updated for mootools 1.3.2

I needed to bring some images into a thumbnail/gallery and decided to use flickr’s API for easy access. The result is a mini-api which allows you to control your options and parse the images that flickr sends back.

// the class
Request.flickr = new Class({
    Extends: Request.JSONP,
    options: {
        callbackKey: "jsoncallback",
        url: "http://www.flickr.com/services/rest/?",
        log: true
    },
    initialize: function(params, options) {
        this.parent(options);
        this.options.url = this.options.url + Object.toQueryString(params);
    },
    success: function(data, script) {
        this.parent(data, script);
    },
    imageURL: function(obj) {
        return "http://farm{farm}.static.flickr.com/{server}/{id}_{secret}.jpg".substitute(obj);
    }
});

// how to use
new Request.flickr({
    format: 'json',
    api_key: "e7df6c74d2545f55414423463bf99723", // your api here
    per_page: 4,
    tags: "mountains",
    method: "flickr.photos.search"
}, {
    onSuccess: function(data) {
        target = $("action");
        var self = this;
        data.photos.photo.each(function(el) {
            new Asset.image(self.imageURL(el), {
                onload: function() {
                    this.inject(target);
                }
           });
        });
    }
}).send();

Want to see it in action? Here’s an embedded jsfiddle:



May 12th 2011 × CKeditor blues: when it fails to pass on the data via AJAX

CKeditor is a good WYSIWYG content editor and is very popular in various CMS and on-page editing tools. It _tends_ to work and is helpful to those who cannot use HTML natively.

However, from a developer standpoint – it can cause some headaches and it lacks proper API (or otherwise documentation). All you get is a brief reference and you can explore the prototypes and guess.

The problems start when you have a form that you handle through AJAX (XHR) that contains CKEDITOR instances for textareas. Intermittently, the submitted data would fail to update the database without any apparent reason.

After some digging, I came to write a fix (through MooTools 1.3) that is incorporated at the form handler prior to the AJAX call. The idea is simple: find any CKEDITOR instances, retrieve them, get the formatted HTML data out and set it as the value of the original textarea element so that the form will save correctly.

document.id("myform").addEvent("submit", function(e) {
    e.stop();
    // find instances, loop through them
    Object.each(CKEDITOR.instances, function(instance, key) {
        // find a form element that has the same id or name attribute
        var el = document.id(key) || document.getElement("name=[" + key + "]");
        if (el) {
            // set matching original element's value to data
            el.set("value", instance.getData());
        }
    });

    // continue with form processing now.
});

I hope it helps somebody as it has been a nuisance for me for a while.


May 10th 2011 × Just a quick heads-up: updated site live header to new sprite animation

It’s been an year since I last touched on the live header malarkey and though the old one was OK, it was more to do with playing with the idea and developing a basic parallax effect in javascript. Oh, and learning more MooTools.

The new one is a complete (or near enough) rewrite of the lemming class. Each lemming is a simple referenced object by the instance, with its own properties and controllable by the instance. The array index of each lemming is the key to a number of pre-defined methods that can help do the storyboard example (along with events the instance or the lemmings fire).

You can view the source and play with them here on jsfiddle: http://jsfiddle.net/dimitar/bYtLz/. The storyboarding was a bit messy but the code is fairly simple. Change var letters = [] to spell something else. Have fun!


May 3rd 2011 × A powerful and simple mootools modal window class called “baseBox”

This was completely overhauled.

Basically, I just released a new mootools modalBox Class called ‘baseBox’. You can see it on jsfiddle and play with it some. Full details, documentation are available on the project github page.

mootools modal box class

Features:

  • Full CSS support – whatever you do, it assigns to the box.
  • Complete control – plenty of options, events and instance references for any scripting scenario
  • Window nesting / grouping
  • Auto toggle when called on the same parent element
  • Smart closing through delegation
  • CSS3 shadows, gradients out of the box.
  • degradation to solid colours and image urls as fallback via CSS
  • CSS2D transform support – scale for FireFox, WebKit, IE9, Opera(untested?) via standard Fx.morph!
  • degradation via a simple fade when not supported

__VERY_ flexible – should be used as a base to extend. For example, to make a class that Extends baseBox that deals with lightBox-like image only display, here’s all you need to do: baseBox.lightBox.js demo. The lightBox demo is also included in the package and is on GitHub.

Similarly, you can make your own sub-classes, for example, baseBox.Request can work asynchronously to get HTML fragments and show them to the user.

Requirements:
MooTools-core 1.3+
MooTools-more 1.3+ - you need Element.delegate and Drag.move

Apr 22nd 2011 × Tutorial: write a small content slider Class in MooTools and extend it

Difficulty: moderate
Framework: MooTools 1.3 (no previous ver compatibility)
Dependencies: MooTools-more 1.3.1 (no compat mode, just Element.Delegation required to build it)
DocType: HTML5 – optional

Before I begin, if you have not made a MooTools Class before, I suggest you first read this tutorial I made last month on creating a Sliding Tips plugin, which is a good entry level.

I imagine you’d want to see what the end result will will look like before you waste your time reading so:
http://jsfiddle.net/dimitar/Ea4Gp/show/ – the Base Class, cross-fading
http://jsfiddle.net/dimitar/Ea4Gp/5/show/ – the Extended Class with alternative transition effect
http://jsfiddle.net/dimitar/Ea4Gp/5/ – the editable fiddle to play with.
preview

Anyway. I just had cause to write a small class that allows me to swap between various content divs/panes that are part of the DOM and allow for any markup/child elements within. I thought I’d share the code as I could not readily find one that did things how I wanted them to be.

First, let us examine the requirements from a HTML point of view: a parent node (element) contains all the divs that will rotate. The Class needs to pick them up and enhance them. Should javascript be available, the user gets to see multiple content divs, otherwise–just the first one (progressive enhancement). Google has to be able to read text and follow any links within the content divs.

The resulting HTML markup looks like this:

<section class="rotator" id="rotator">
    <div class="pane" style="background-image:url(http://fragged.org/img/home/hunting-home.jpg)">
        <div class="info">
            <h2>Hunting CS style</h2>
            Built for your `camping` pleasure.
        </div>
    </div>
    <div class="pane hide" style="background-image:url(http://fragged.org/img/home/fishing-home.jpg)">
        <div class="info">
            <h2>Fishing? Really? </h2>
            Fishing is for mongs. <a href="#">Click here</a>
        </div>
    </div>
    <div class="pane hide" style="background-image:url(http://fragged.org/img/home/tourism-home.jpg)">
       <div class="info rambling">
           <h2>Rambling and walking</h2>
           Wish you were here? Can't blame you, it's lame.
        </div>
    </div>
</section>

The accompanying CSS that goes along with the markup above:

section.rotator {
    border-bottom: 4px solid #000;
    width: 700px !important;
    height: 280px !important;
    overflow: hidden;
    position: relative;
}

section.rotator div.pane {
    position: absolute;
    width: 700px;
    height: 280px;
    background-repeat: no-repeat;
    background-position: left top;
}

Come the MooTools magic. The Class is _very_ simple:

this.contentSwapper = new Class({

    // mootools options and events mixins as standard
    Implements: [Options, Events],

    // some defaults...
    options: {
        delay: 3000,
        selector: "div",
        controlLeft: "http://fragged.org/img/home/moveLeft.png",
        controlRight: "http://fragged.org/img/home/moveRight.png"
    },

    initialize: function(element, options) {
        // base constructor.

        // check to see if it's called with an element...
        this.element = document.id(element);
        if (!this.element)
            return;

        this.setOptions(options);

        // grab the child divs
        this.elements = this.element.getChildren(this.options.selector);
        this.index = 0;

        // call some methods we are about to define.
        this.attachControls();
        this.startRotation();
        this.attachEvents();
        this.fireEvent("ready");
    },

Once again, I like to break up all the things the Class does into small methods – ideally, as small as possible without getting into ridiculous territory where you write a method that returns a property you can access directly (eg, this.getSize = function() { return this.size; }).

attachControls is a cool method, as it makes use of some of MooTools 1.3′s new features: Slick and zen-like element construction.

attachControls: function() {
    this.controls = $$(
        new Element("img#moveLeft.contentControl[title=Previous][src={controlLeft}]".substitute(this.options)).inject(this.element, "top"),
        new Element("img#moveRight.contentControl[title=Next][src={controlRight}]".substitute(this.options)).inject(this.element, "top")
    );
},

Notice the $$() around the controls. This will create a MooTools HTML collection that is iteration-able (.each or any methods). The collection is basically like an array with added Element prototypes working. Other than that, nothing too complicated takes place – makes a new image with id #moveLeft, class .contentControl, sets the title and the src property.

To attach events: we want the rotation to stop when the user mouses over the main element (section.rotator) so they can read in peace and possibly click any call to action it may have.

attachEvents: function() {
    this.element.addEvents({
        mouseenter: this.stopRotation.bind(this),
        mouseleave: this.startRotation.bind(this),
        "click:relay(img.contentControl)": this.move.bind(this)
    });
},

Hence, mousenter calls our stop method and mouseleave starts again. The “click:relay(img.contentControl)” is MooTools’ way to doing Event Delegation. Saves us you having to bind 2 events and callbacks to both controls by just binding to the parent element.

The movement methods. Very minimal logic, they literally just set variables.

move: function(e, el) {
    // based upon image id, it will match a method name.
    this[el.get("id")](); // call it dynamically
},

moveLeft: function() {
    // set the next frame coming to previous one or last
    var next = (this.index == 0) ? this.elements.length-1 : this.index-1;
    this.swapFrames(next);
    this.fireEvent("left");
},

moveRight: function() {
    // set the next frame coming to next one or first
    var next = (this.index < this.elements.length-1) ? this.index+1 : 0;
    this.swapFrames(next);
    this.fireEvent("right");
},

Now we get to the more interesting parts. The actual swapping of the content is the core of the Class and is what we will modify later when we extend it to make it work differently.

The base class version does a VERY simple transition based around tweening the opacity of the 2 relevant content panes. One fades in while the other fades out.

swapFrames: function(next) {
    // element currently visible is in this.index (as key of this.elements array)
    var curEl = this.elements[this.index];

    // clean up from before, just in case
    curEl.get("tween").removeEvents();

    // reset element and set tween options.
    curEl.set({
        "tween": {
            link: "cancel",
            onComplete: function() {
                // add a css class that sets display to none, for example
                this.element.addClass("hide");
            }
        },
        styles: {
            opacity: 1 // initial opacity reset in case quick clicks
        }
    }).fade(0);

    // now set our new visible frame from this.moveLeft/Right argument
    this.index = next;

    var newEl = this.elements[this.index];
    //clean up, reset, show and fade in.
    newEl.get("tween").removeEvents();
    newEl.setStyle("opacity", 0).removeClass("hide").fade(1);
}

The end result: panes will cross-fade between each other, giving a morphing impression. Less is more, as they say.

Finally, we have 2 small methods that start and stop the automatic rotation. To keep the user interested in your site content, you really need to take no more than 2.5 second from their initial impression of the site or they may _subconciously_ lose interest and just bounce based upon the failure to connect with content shown from the start.

startRotation: function() {
    clearInterval(this.timer);
    this.controls.fade(.5);
    this.timer = this.moveRight.periodical(this.options.delay, this);
    this.fireEvent("start");
},
stopRotation: function() {
    clearInterval(this.timer);
    this.controls.fade(1);
    this.fireEvent("stop");
}

// and end class...
});

The controls collection we did earlier fades up and down based upon interaction: when it's stopped, the user is 'over' the element so we make the left and right more visible and we fade them out afterwards so they are less distracting.

This is it. To call the Class with your default options, all you need is:

new contentSwapper(document.id("rotator"));

You can, of course, attach event callbacks that respond to the ones the class has fired:

new contentSwapper.Fancy(document.id("rotator"), {
    onReady: function() {
        console.log("we are up!");
    },
    onStart: function() {
        console.log("and we are moving");
    },
    onStop: function() {
        console.log("Oh! they are reading with mouse on top!");
    }
});

... and so forth and so forth.

Simple. Now, imagine you are happy with your base Class but would like to extend it somewhat, by modifying it slightly to better suit your needs. In this case, we no longer desire to have a standard opacity cross-fade transition between panes so we will try to override that.

MooTools allows you to use the Extends: mutator(?) as part of your class declaration.

contentSwapper.Fancy = new Class({

    Extends: contentSwapper,

    initialize: function(element, options) {
        this.parent(element, options);
    },

We have now extended the base class and have everything it does inherited. But we want to actually override one of the methods to make things snazzy. We will try to make a vertical transition of the new pane over the old one as an experiment.

swapFrames: function(next) {
    var curEl = this.elements[this.index];
    // using morph for multiple CSS properties we will adjust
    // on the same timer, eg, opacity and margintop:
    curEl.get("morph").removeEvents();
    curEl.set({
        styles: {
            zIndex: 1000,
            opacity: 1
        },
        "morph": {
            link: "cancel",
            duration: 1000,
            onComplete: function() {
                this.element.addClass("hide");
            }
        }
    }).morph({
        // we really just want opacity as it looked better but experiment
        opacity: 0
    });

    this.index = next;

    // bring the new element in from the top, 280px.
    var newEl = this.elements[this.index];
    newEl.get("morph").removeEvents();
    newEl.removeClass("hide").setStyles({
        zIndex: 1001,
        marginTop: -280,
        opacity: 0
    }).morph({
        marginTop: 0,
        opacity: 1
    });
}

}); // end extended class

That's it. Now, you can improve that further by setting the marginTop offset into the Fancy options or read the height of this.element instead, your call.

To use the alternative and fancier way of transitioning, you do:

new contentSwapper.Fancy(document.id("rotator"));

Supplemental CSS required:

img.contentControl {
    position: absolute;
    margin-top: 90px;
    z-index: 10000;
    cursor: pointer;
    _cursor: hand;
}
#moveLeft {
    margin-left: 0;
}

#moveRight {
    margin-left: 662px;
}

Dead simple, isn't it? Have fun with MooTools. Any comments, questions or feedback, @D_mitar on Twitter or coda- on #mootools (irc.freenode.net).


Mar 12th 2011 × Tutorial: write a small but flexible MooTools Tips class

The other day somebody came in on the IRC channel and asked if there’s a MooTools script available to replicate the effect of this jQuery plugin.

The main objective was to be also easily able to keep the tip on whilst the mouse skips from the trigger element into it. Since I did not know off the top of my head of an adequate MooTools class to perform the task, I simply took 30 mins to write one. This is to document that and explain the decisions behind it.

To get an idea what our class will do when done, view http://jsfiddle.net/dimitar/WUfNR/show/ or look at the source code on git.

As a disclaimer: this is NOT a port of the jQuery plugin mentioned. I have not looked at the source code and simply am replicating the effect and using their graphics for convenience’ sake.

Also, this mini-tutorial or (even code-review) is aimed at people who already have rudimentary knowledge of Javascript, MooTools, CSS, DOM and understand the concept of classic OOP. Recommended read on how MooTools deals with OOP and the Class constructor in general is Keeto’s Up The Moo Herd III article.

First: decisions, decisions. Planning ahead is important so… The easiest way to achieve the effect we are after without firing onmouseout when moving the mouse into the ‘tip’ is to have the tip become a child element of the trigger element. To understand that better, consider it as a simple HTML/CSS markup solution where a child div has absolute positioning and some negative marginTop. Although this is very easy, it is a somewhat limiting decision as it means the tip element can only be injected into the DOM with a parentNode that can have children. input, img, textarea are not good for us to attach to directly. It’s not that much of a drama as you can wrap them into spans or divs, which will work fine.

The start: creating the MooTools class skeleton

this.tippable = new Class({

    Implements: [Options, Events],

    options: {
        tipClass: ".tippable",
    },

    initialize: function(element, options) {
        // public instantiation

        this.setOptions(options);
        this.element = document.id(element);
        if (!this.element)
            return;
    }

});

What is happening here? Basically we are defining a new MooTools Class called ‘tippable’. Within that class we use the MooTools Options and Events classes as mixins. The Options class is useful for merging options from the instance that override the default class options property by doing an `Object.merge(options, this.options);`. This functionality is available through the `this.setOptions(options)` call and is a great practice that allows you to setup defaults that can be changed by the instance. The Events mixin is another very handy Class that brings Events (surprise) into your classes and can be used in conjunction with options object you pass on. The premise is simple: whenever something ‘awesome’ happens in your class that the instance should know about, you can do `this.fireEvent(“awesome”, [params])` and if on your options object on your class instance you have a property `onAwesome: function() { … }`, it will get passed with the scope of the class itself. More on this later.

Maintaining your MooTools Classes requires you to adhere to some unwritten rules that make it easier for developers to follow what’s going on. From that point of view, it’s an accepted convention that if your class deals with a particular element, it should be referenced as `this.element`. An array of elements would be… `this.elements` and so forth (hint: read the linked article above).

We are also making sure that an element has been passed and it’s a DOM node, something that the `document.id()` call ensures. It’s unsafe for the Class to continue if it can’t find the element so it exits quietly. You may want to trigger an exception here or degrade gracefully – depends on your requirements.

Building your Class

It’s now time to add our first ‘real’ method. I like to break the Class down into multiple small methods that are self explanatory and do as little as possible. This is a great practice for many reasons: it means your code is self explanatory and reduces the need for leaving comments. It also allows you to create patterns that you can call again and again. We are going to create a new method we are going to call `attachTip`

attachTip: function() {
    // call creation and events addition
    this.createTip();
    this.attachEvents();
},

As you can see, it will call two more methods we will add: one that creates the tip into the DOM and one that deals with the trigger events.

I am going to post the finished version of the method and it will reveal references to options and methods we have not mentioned yet. Basically – as a practice, I tend to add to my default options object as I see fit whilst I am writing the code and come to a decision about using a static value or a variable that may be useful for the user to override.

createTip: function() {
    // add tip to dom and set initials

    // first event will be onShow
    this.event = "show";
    this.tip = new Element(this.options.tipClass, {
        styles: {
            opacity: 0
        }
    }).set("morph", Object.merge(this.options.fx, {
        onComplete: function() {
            this.fireEvent(this.event);
        }.bind(this)
    }));

    // store title
    this.title = new Element("div.title").inject(this.tip);

    // tip body
    this.body = new Element("div.body").inject(this.tip);

    // append to DOM
    this.tip.inject(this.element, "top");

    // store instance into the tip element for event use!
    this.element.store("tippable", this);

    // set initial title and body values
    this.setTitle(this.options.title);
    this.setHTML(this.options.text);

    // now to position element
    if (this.options.leftOffset === false) {
        var tipWidth = this.tip.getSize().x, elWidth = this.element.getSize().x;
        this.tip.setStyle("marginLeft", (elWidth - tipWidth) / 2);
    }
    else {
        this.tip.setStyle("marginLeft", this.options.leftOffset);
    }

}, // end createTip

As I just pasted this, it occurred to me it would be useful to allow users to define their own CSS ClassNames for the tip title and tip body, even if they can be targeted in CSS as `value-of-this.options.tipClass div.title {..}`. Anyway, that’s a change you can quickly do. Similarly, we are going to be using certain variables passed through options or defaults. The default options for the Class now look like this:

options: {
    // NB: these are zen classes for Slick
    tipClass: "div.tippable",
    text: "",
    textClass: "div.body",
    title: "",
    titleClass: "div.title",
    topOffset: 110, // where it stops above the element
    topOffsetStart: 160, // where the animation starts from.
    leftOffset: false, // if false, center over element, else, integer
    // Fx class options
    fx: {
        duration: 400,
        link: "cancel"
    }
},

To reiterate: if you do not pass an options object to the Class instantiation, it will still work with the default values. If you want to override a single option, you do that and the rest are defaults. The key / interesting options we use are these:

  • tipClass, textClass: a ‘zen-coding like’ element hook to create and style the tip container (by ID or CSS reference). In MooTools 1.3, Slick can work with Element in reverse, you can even declare things like `new Element(“div#someid.foo[html=hi]“);`
  • fx: an object that provides the ability to pass on options to the Fx.Morph instance on the tip element such as duration, Fx.transition and so forth.
  • topOffset and topOffsetStart: can control the ‘animation’ path – where to start from and how far above (or below) the element to stop. By using negative values here, you can do a tip that flies from bottom to top instead…
  • leftOffset: that value I added later when I realised I may not always want to try and center the tip above the trigger element but may want to output it to the left at nn pixels. if False, centered, else, marginLeft becomes leftOffset instead.
  • text / title: Last but not least, the default text body and title for the tip.

Back to the function. In a nutshell, when attaching the tip to the DOM, it makes a new div, say div.tippable (styled through CSS alone) and makes it invisible by default. It also defines the Fx morphing options and helps out a little with the events it will fire onShow and onHide. I will go in more details over why events are important and how you can use them in the instance.

The tip is injected – as stated before – as a child node of the trigger element – so it needs to be styled via CSS to have position: absolute.

An interesting thing that I do here is I also store the tip instance into the trigger element storage (via this.element.store(“tippable”, this);). This allows me to programatically control the tip instance from external javascript by simply having access to the element where I can get the instance by `var instance = doing elObj.retrieve(“tippable”);`. It’s great so that I don’t have to store each instance into a separate variable into my scope in case I want to remove it, modify it or show a tip without mouse events.

Another interesting thing is I have added public methods which allow for the changing of the tip title and body via setTitle and setHTML. This means you can control the content and change it dynamically or even bring in tip content through events and ajax, which I will show later on. Back to the Class, we are adding these two simple methods:

setTitle: function(what) {
    // set the title content public api
    if (what.length) {
        this.title.set("html", what);
    }
    return this;
},

setHTML: function(what) {
    // set body content public api
    this.body.set("html", what);
    return this;
},

Please note the return this; at the end of some of the methods. This is another unwritten rule in the MooTools world: allow for chaining. By having the Class instance as the exit/return point of a method, it allows you to chain more methods to it. In other words, you can do on a single line:

this.setTitle("hello").setHTML("this be the tip text, ahoy!");

When dealing with DOM events pointing back to your class methods, things are slightly trickier at first:

attachEvents: function() {
    // private
    this.boundEvents = {
        mouseenter: this.showTip.bind(this),
        mouseleave: this.hideTip.bind(this)
    };
    this.element.addEvents(this.boundEvents);
    return this;
},

detatchEvents: function() {
    this.element.removeEvents(this.boundEvents);
    return this;
},

What takes place here is: on attachEvents we set mouseenter and mouseleave to point to the special methods we will create to show or hide the tip respectively. Because these two methods need to keep the scope to the class instance, we bind them to ‘this’. Because we need to be able to remove the tooltips from an element and clean up the events, we store a reference to the bound functions into the class instance so that detachEvents can find the callbacks and remove them (in this.boundEvents).

We are happy now, we can create the class and attach events to it. So here are the two functions that deal with showing and hiding the tip:

showTip: function() {
    // triggered on mousenter or called direct

    // fire onBeforeShow
    this.fireEvent("beforeShow");

    // next event will be onShow through morph onComplete
    this.event = "show";

    // animate properties
    this.tip.morph({
        marginTop: [-this.options.topOffsetStart, -this.options.topOffset],
        opacity: [0, 1]
    });
    return this;
}, // end showTip

hideTip: function() {
    // triggered on mouseleave ot called direct

    // fire onBeforeHide
    this.fireEvent("beforeHide");

    // next morph event will be hide
    this.event = "hide";

    // hide animation
    this.tip.morph({
        marginTop: -this.options.topOffsetStart,
        opacity: [1, 0]
    });
    return this;
} // end hideTip

The first thing we do is we fire off the ‘before’ events. This can tell the instance we are about to start animating the tooltip into view or out of view. Why is this useful? You may want to modify your trigger element and make it an ajax spinner, change some content, modal your screen or even request data from ajax to show later.

this.event = “show”; is a little hook that allows us to pass back to the Fx.Morph onComplete event which event to fire next, in this case onShow. To the instance, that event says, ‘animation is complete and tip is in full view’. One thing you can do here is, for example, passing this to the options when instantiating the Class:

onShow: function() {
    this.setTitle("").setHTML("loading...");
    var self = this;
    new Request({
        url: "someurl.php",
        onComplete: function() {
            self.setHTML(this.response.text);
        }
    }).send("view=getTip&id=" + this.element.get("data-id"));
}

… will fetch some data from an ajax view with some params and then set the tip body text to the response.

The hideTip fires the opposite events, onBeforeHide and onHide. An example use case for onHide can be to make it a one-off tip and remove the tippable after it has been seen:

onHide: {
    this.deatchEvents();
}

And done! Subsequent mouseenters on that element will no longer trigger a tooltip.

That’s it, in a nutshell. You have just created your new MooTools Class and can start using it. Look at the jsfiddle for this tutorial, it is somewhat important to look at the CSS that accompanies the Class:
http://jsfiddle.net/dimitar/WUfNR/. You can also see the events at work (look at your console) and try a a double-click on a trigger image to disable the class.

Any questions, you can always find me on #mootools on irc.freenode.net (nick coda- or d_mitar) or… just ask. I am sure somebody will be there to help. Have fun