Follow me: @D_mitar

Most read posts recently



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


Jan 20th 2009 × getting fancy with AJAX: your first form submission and verification

Having frequented the Digital Point Forums as of recent, I have come to the conclusion that most people are barely coping with the concepts of javascript and AJAX. So much so that they are struggling to interpret anything that is more advanced than changing a few field names and including some javascript files in the page header.

Naturally – people wanting it to ‘just work’ are right, of course. There will be plenty of solutions out there for most things that can do this and achieve it in such a way that does not warrant an understanding of any javascript concept. But there comes a day when you go ‘hold on, what if I need to do this?’ and find you are stuck with a solution that you cannot comprehend…

I guess all my posts thus far have been aimed at a different type of person – one that is more ‘at home’ with javascript and mootools. Time to go back to basics…

*edit* if you’ve come here for the magical email regex that will cover _any_ RFC compliant email, here it is:
var validEmailRegex = /^(([^<>()[\]\\.,;:\s@\”]+(\.[^<>()[\]\\.,;:\s@\”]+)*)|(\”.+\”))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

click here to view sample tests and code

The most common use for AJAX that seems to draw the crowds is still a form submission. It can be via GET or via POST, it may do some form processing for required fields and so forth. But it needs to submit without a page reload and it needs to output something back. So, how do you set about creating an AJAX form?

1. First of all, grab a copy of a javascript framework. A framework is simply a library of pre-set functions and methods you can use in your own pages. I tend to use mootools but anything will do – read my post on selecting a framework. In this example, we WILL use mootools, however.

2. Create your form page. As basic as this example is, we need to assume at you have at the very least the rudimentary skill of being able to compose a form in HTML. What you should try to do is to get your form working and submitting data to your PHP or ASP or CGI processor page on its own – and we will then convert your page into an AJAX submission instead. Here is an example form page:


<div id="feedback" style="background: #eee; padding: 2px; border: 1px solid #333; float: left">
    <form action="mooForm.php?a=send" method="POST" id="myform">
    <label for="customer_name">Your name:</label><p>
    <input type="text" class="required bordered" value="" name="customer_name" id="customer_name"/><p>
    <label for="customer_email">Your email:</label><p>
    <input type="text" class="required bordered" value="" name="customer_email" id="customer_email"/><p>
    <label for="customer_comment">Your message:</label><br />
    <textarea class="required" style="width: 360px" name="customer_comment" id="customer_email"/></textarea><br /><br />
    <input type="submit" value="Send data" /></form>
</div>

3. Create a wrapper element for the form where we will output the reply: when contact_us.php is ‘reached’ and the data is saved, it can send us feedback to this effect – something along the lines of: “Thank you for your submission”. To do so, just add <div id=”feedback”></div> around your form – or you can keep it to the side, up to you and your styling preferences. We are going to be ‘replacing’ the form with the reply, however. Here it is how it all looks and works ‘on-screen’:


4. Attach javascript to intercept the form submission: you need to take control of what happens once the user presses “Send data”. Do this by fetching a copy of mootools and uploading it to your server. Within the head section of your site, you’d add the following:

<script type="text/javascript" src="mootools-1.2.1core.js"></script>
<script type="text/javascript">
// in mootools, the 'onload' event is called 'domready' and it ensures
// the DOM has completed loading and elements can be modified safely.
window.addEvent("domready", function() {
    // now, add events for the form
    $("myform").addEvents({
        "submit": function(e) {
            // stop the event from propagation...
            e.preventDefault();

            new Request({
                url: this.get("action"),
                data: this,
                onComplete: function() {
                    $("feedback").set("html", this.response.text);
                }
            }).send();
        }
    });
}); // end domready
</script>

Although, you can leave it at that, to get a working AJAX form

5. Expand on your code to add some error checking. Of course, you can use PHP or ASP or whatever serverside language you like to inspect the data being submitted – and probably should do so anyway. But it helps if you can add some trapping clientside as well – on the off-chance it reduces errors and keeps your server load lower.


// extend elements to support warnings within values
Element.implement({
    fieldWarning: function(warningText, warningDelay) {
        // very basic - changes the value of the input to warningText
        // restores to "" within warningDelay in ms

        this.set({
            value: warningText,
            styles: {
                backgroundColor: "#fcc"
            },
            events: {
                focus: function() {
                    this.set({
                        value:  "",
                        styles: {
                            backgroundColor: "#fff"
                        }
                    }).removeEvents();
                }
            }
        });

        (function() {
            this.fireEvent("focus").removeEvents();
        }).delay(warningDelay, this);
    }
});

var validEmailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

// define common functions and variables
var isValid = function(email) {
    // boolean function that checks email string against allowed strings in accordance to RFCs
    return email.test(validEmailRegex);
},  // end isValid
cleanData = function(elementsArray) {
    // cleans passed elements with property 'value' from spaces and XSS
    elementsArray.each(function(el) {
        el.set("value", el.get("value").clean().stripScripts());
    }); // end each
} // end cleanData

window.addEvent("domready", function() {
    // now, add events for the form
    $("myform").addEvents({
        "submit": function(e) {
            // stop the event's default action from propagation...
            e.preventDefault(); // can also do new Event(e).stop();

            // clean all form fields from excess spaces and cross side scripting attacks
            cleanData($("myform").getElements("input,textarea"));

            var noErrors = true;

            // first, loop required elements
            $$("input.required,textarea.required").each(function(el) {
                var testedValue = el.get("value");
                if (testedValue.length == 0 || testedValue == "Required field") {
                    noErrors = false;
                    el.fieldWarning("Required field", 2000);
                }
            });

            if (noErrors) { // test email
                var testedEmail = $("customer_email").get("value");
                if (!isValid(testedEmail)) {
                    $("customer_email").fieldWarning("INVALID: " + testedEmail, 2000);
                    noErrors = false;
                }
            }

            // if no errors in the form, compose the XHTTP request manually.
            if (noErrors)
            new Request({
                url: this.get("action"),
                data: this,
                onComplete: function() {
                    $("feedback").set("html", this.response.text);
                    // since the form itself is contained within this layer
                    // it will 'self destroy' with whatever HTML we print
                    // from the PHP/ASP.
                }
            }).send();
        }
    });

});

What is taking place here? For the purposes of this small form, we are going to perform 2 types of javascript validations: a required field (which they all are) – a check to see if the length of the strings entered is greater than zero; and an email verification test – a check to see if the email string is compliant with RFCs, done via a regex test.

You can download the javascript above by clicking here and look at it at your own leisure.


Sep 29th 2008 × What is a javascript framework and why do I need one, part I

This ‘article’ is aimed at helping webmasters, programmers and designers who already have a rudimentary understanding of javascript and DOM programming in cementing their knowledge and getting a solid base for building browser apps in the future. For illustration purposes, I shall be using the MooTools framework but this does not mean it’s the only sensible choice.

I have been posting a fair bit on DigitalPoint’s javascript forums recently and was amazed to discover (well, not really) the huge amounts of people that struggle to get basic javascript bits and bobs working. Adding a javascript framework won’t necessarily fix that all of these problems but a good half of them (at least) will get ‘nearly there’. It also proven to improve manhood tenfold…

So, what is a javascript framework anyway?
In a nutshell: a library of pre-written javascript controls, functions and methods that make it easier for the developer to quickly and accurately produce cross browser compliant code. Example popular frameworks are: YUI, ExtJS, Prototype, MooTools and of course, jQuery. There are also several smaller frameworks around – have a look on ajaxian for a recent rundown of the relative unknowns (like Capuccino)

Yeah, and? What do I need all these libraries for?

Selectors

making sure you can access any and all of your DOM elements at your fingertips

Typically, frameworks provide all sorts of shortcuts for accessing elements within the DOM. These are called selectors and are similar to CSS selectors. Example:

<div id="mydiv" class="myclass" rel="89">click me</div>
<div id="mydiv2" class="myclass" rel="90"><a href="#">null link #0</a></div>
<span id="myspan" class="myclass" rel="89">hello world 3</span>

<a href="#">null link #1</a>
<a href="#">null link #2</a>
<a href="/index.php">normal link</a>
<input id="name" type="text" class="textfield" />
<input id="surname" type="text" class="textfield required" />*
<textarea id="message" class="required"></textarea>*

… and mootools selectors on the above

var div = $("mydiv"); // just reference by id, same as document.getElementById("mydiv"); 

// get an array of all elements that are 'myclass' (mydiv, mydiv2, myspan)
var elsArray = document.getElements(".myclass"); 

// get an array of div elements that are 'myclass' (mydiv, mydiv2)
var elsArray = document.getElements("div.myclass"); 

// get an array of elements that are myclass and rel == 89 (mydiv, myspan)
var elsArray = document.getElements(".myclass[rel=89]");

// get an array of elements that are myclass and rel is not 89 (mydiv2)
var elsArray = document.getElements(".myclass[rel!=89]"); 

// get the first element that has class myclass and rel=89 (mydiv)
var elObject = document.getElement("myclass[rel=89]");

// get an array of null links (to anchors) - (null links #0,1,2)
var links = document.getElements("a[href=#]");

// all links within the mydiv2 layer only
var localLinksArray = $("mydiv2").getElements("a"); 

// get all required fields (surname, message)
var requiredFields = document.getElements("input.required textarea.required");

The examples for selectors are potentially endless as are the possiblities for use. There is nothing to prevent you from using the old document.getElementById() function, or even document.getElementsByTagName(). There is even a new function coming out now to a browser near you (perhaps) – getElementsByClassName(). But that’s just the point – by using a framework, you are guaranteed compatibility NOW and with future releases. If a new browser ‘Steel’ comes on the market that provides access to its DOM elements by way of a function document.ById(), you would not have to go and update every single bit of javascript that on your sites to provide support for it – all you’d do is update your framework and grab a coffee – the major frameworks will always pickup on browser changes and new versions. In fact, I expect most are readying updates to cover the new Google Chrome browser and webkit changes.

Compatibility and performance

Different browsers and different versions have always had their old ideas about what’s the best way to ‘do things’. One can’t help but apploud the peioneering spirit of the Internet Explorer engineers, who pretty much just made stuff up as they went along and ignored many a WC3 standard in the process… The thing that frameworks give you that you will learn to appreciate the most is a bridge between all the browsers. You no longer need to worry if IE6 will fire an onload event when other browsers fire oncomplete. You also won’t have to worry about mistakes in misfiring of onmouseout events when hovering over child elements (IE bug, of course). Frameworks do the worrying for you and fix those common errors that are deemed fixable.

As for performance, it really, really can’t get much better than relying on a bunch of crazed javascript coders and programmers, many of whom are involved in the development of Mozilla, to write, refactor and improve all your functions for you! You get what you want from the DOM, in the fastest possible way. Yes, I am talking ‘selectors’ again and there are lots of test suites / javascript framework benchmarking results of all the major frameworks. More or that will come in part 2 – for now, suffice to say: doing a for() loop through your elements just won’t cut it.

AJAX out of the box

Enough selecting, it’s time to use some keywords that spell ‘magic’ to any marketing pro out there: ajax. web 2.0. ooooh….

Most of today’s modern frameworks will have a way of dealing with XHR requests, more commonly referred to as AJAX. Whereas it does not take much to get going with a simple AJAX request, frameworks have it polished into perfection. You can easily set things like:

  • method (GET / POST)
  • target url
  • setting up events (start, sucess, failure)
  • parameters to url as GET or POST data
  • displaying desponse into a layer/table element
  • evaluating response as javascript in a safe way
  • processing response AND evaluating <script> tags

    Say, you have an element with some summary text and you’d like to attach a click event to it to show the full article text. Here is how that AJAX request can look in mootools:

    $("mydiv").addEvent("click", function() {
        // make a click on mydiv2 to callup an ajax function that gets
        // the full contents of the layer replaced and runs through some scripts
        new Request({
            url: "/index.php",
            method: "get",
            evalResponse: false,
            evalScripts: true,
            update: $("mydiv"), // replaces the text there at the moment
            onComplete: function() {
                // already updated to full text, no need for the event to remain
                $("mydiv").removeEvents();
            }
        }).send("action=getFullText&id="+this.get("rel"));
    });

    Same functionality (just the AJAX call) in jQuery would go like so:

    $.ajax({
      url: "index.php",
      cache: false,
      success: function(response){
        $("#mydiv").append(response);
      }
    });

    In part 2, we will review how to code a few practical snippets that can build you a core library for your site – things like, tooltips, bubbles, form checkers, expanding and collapsing menus and more. And of course, my advice on choosing the right framework for you (always a subject of debate and controversy)