Follow me: @D_mitar

Most read posts recently



Mar 31st 2010 × mooSelecta – an accessible mootools select/dropdown element replacement

Don’t you just love how different browsers style (or rather, don’t style) select and option elements? One of the things I dread most as a developer is when the design team come in and produce forms with controls that are totally bespoke and unobtainable due to the browser limitations.

This prompted me to write a mootools class that replaces the existing select items on the page with custom style-able controls through CSS as a progressive enhancement. Here is an example of what the resulting dropdowns can look like:

mooSelecta dropdowns screenshot

The idea of skinnable dropdowns is nothing new but not many solutions are done in a way that mimics the select element’s behaviour and events correctly. Here is what mooSelecta does feature:

  • support for any type of skins, all through CSS
  • support for native element events (will work with scripting)
  • support for keyboard look-ups when dropdown is open
  • support for input labels
  • support for tabindex

Have a look on the mooSelecta example page or on the mootools forge page for the plug-in itself (download etc).

One thing you need to be aware of is that if you want to dynamically change the selected value of a skinned select, you need to fire it’s onChange event for mooSelecta to update. For example, if you have a dropdown with id=”country” and class=”selecta”:

new mooSelecta({
    selector: "selecta"
});

// change default selected value via js later:
$("country").set("value", "United Kingdom").fireEvent("change");
// the event will make mooSelecta update

Aside from that, it’s fine, tested and working in IE6, IE7, IE8, Firefox 2, Firefox 3.6, Safari 4, Chrominum 5. Known issues with Opera keyboard events not being raised as expected. Enjoy!


Mar 22nd 2010 × Another javascript test

This time I found a fun javascript test on StackOverflow on the PerfectionKills blog: Javascript Quiz, once again it has been suggested to use through the interview process for a javascript / front-end engineer role.

Good luck and have fun (as they say…)


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:


Mar 10th 2010 × Cross-domain AJAX calls via YQL as proxy and mootools JSONP

I wrote this a while back when I needed to do pseudo AJAX calls to a remote host. Due to XSS restrictions and security policies, a normal XMLHttpRequest (XHR) call is not allowed to work across domains or even sub-domains. But Yahoo’s YQL interface kindly lets you GET any URL (which also means being able to submit via GET), irregardless of whether the domain is local or not.

Here is how it works. You need mootools (also download mootools-more as this class will extend Request.JSONP which is a part of mootools-more).

Request.YQLajax = new Class({
    // gets basic info such as country and latitude data
    Extends: Request.JSONP,
    options: {
        log: !true,
        url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22{location}%22&format=xml"
    },
    initialize: function(location, options) {
        this.parent(options);
        if (!location)
            return;

        this.options.url = this.options.url.substitute({location: encodeURIComponent(location)});
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

// example use to fetch BBC's page
new Request.YQLajax("http://www.bbc.co.uk/", {
    onSuccess: function(data) {
        $("result").set("html", data.results);
    }
}).send();

You can see this in action on mooshell or play with it below. Happy cross-domain ajax-ing.


Mar 4th 2010 × Getting latest tweets through mootools JSONP

Nothing like being egocentric so here’s what you can do to add latest tweets of a particular user to your site through mootools. This class totally `forked` from a post by AppDen (Scott Kyle) so feel free to give him some love, he deserves it for being such a bright spark. You can also “play” with this live on JSFiddle

Request.twitterAPI = new Class({
    // return json data with latest tweets form a particular user
    Extends: Request.JSONP,
    options: {
        target: $empty(),
        tweetBits: {
            textClass: "tweetBody",
            dateClass: "tweetDate"
        },
        maxTweets: 4,
        autoLink: true,
        url: "http://twitter.com/statuses/user_timeline/{user}.json"
    },
    initialize: function(user, options) {
        this.parent(options);
        this.options.url = this.options.url.substitute({user: user});
        this.element = document.id(this.options.target);
        if (!this.element)
            return;

        this.element.set("html", "Loading tweets...");
    },
    success: function(data, script) {
        this.element.empty();
        this.parent(data, script);
    },
    showPost: function(post) {
        var creationDate = Date.parse(post.created_at).format("%x %X");
        new Element("div", {
            "class": this.options.tweetBits.textClass,
            "html": this.options.autoLink ? this.linkify(post.text) : post.text
        }).adopt(new Element("div", {
            "class": this.options.tweetBits.dateClass,
            "html": creationDate // + " via " + post.source.replace("\\",'')
        })).inject(this.element);
    },
    addPosts: function(response) {
        if (!response.length || !this.element)
            return;

        response.each(function(post, i) {
            if (i < this.options.maxTweets)
                this.showPost(post);
        }.bind(this));
    },
    linkify: function(text){
        // modified from TwitterGitter by David Walsh (davidwalsh.name)
        // courtesy of Jeremy Parrish (rrish.org)
        return text.replace(/(https?:\/\/[\w\-:;?&=+.%#\/]+)/gi, '$1')
            .replace(/(^|\W)@(\w+)/g, '$1@$2')
            .replace(/(^|\W)#(\w+)/g, '$1#$2');
    }
});

// create an instance
new Request.twitterAPI("D_mitar", {
    target: "myTweets", // target element to 'host' the tweets
    onComplete: function(data) {
        this.addPosts(data); // add all posts to element.
    }
}).send();

Additionally, you can style the tweets by using 2 classes for the element:

div.tweetBody {
    line-height: 1.1;
    font-family: arial;
    font-size: 12px;
    margin-bottom: 10px;
}

div.tweetDate {
    font-size: 10px;
    color: #500;
    text-align: center;
    padding-top: 4px;
}

That’s it, it’s that simple. Happy tweeting.


Feb 19th 2010 × javascript tests fun continues

A great new test through javascript.ru (an incredible resource, even if in Russian). Dmitry Soshnikov has created a new fun javascript quiz you can test your knowledge against. The difference is that he’s tried to abstract real world problems, collected from various mailing lists or problems of his own doing. Worth 10 mins for sure.


Feb 15th 2010 × Creating a wrapper namespace for the FireBug console.log function for IE and other browsers

Most web application/AJAX development takes place within the confines of either FireFox + Firebug or Webkit browsers and their consoles (firebug is now available on chrome?). However, other browsers (such as IE) lack console functionality and this presents a problem.

When in a hurry / under pressure, I have been known to forget to take out a console reference out of code that sneaks it’s way into the production environment. If not immediately noticeable, tucked away behind a loop / if statement, you’d never know about it… That is, until the error reports start flooding in.

Not wanting to find myself in such position again, I decided to write a little wrapper that can cater for nearly all of firebug’s console methods. The most common ones, such as console.log, console.info, console.clear and console.count are pre-defined but you can use the model and add others into the singleton. Check this fine firebug cheatsheet for more ideas on other methods you may want to port.

this.$C = {
    // console wrapper by D. Christoff @ http://fragged.org/
    _version: 2.4,
    debug: true, // global debug on|off
    quietDismiss: true, // may want to just drop, or alert instead,
    method: "log",
    _hasConsole: function(_method) {
        var _method = _method || "log";
        return typeof(console) == 'object' && typeof(console[_method]) != "undefined";
    },
    _consoleMethod: function() {
        if (!this.debug) return false;

        if (this._hasConsole(this.method)) {
            console[this.method].apply(console, arguments);
        } else if(!this.quietDismiss && arguments.length) {
            var result = "";
            for (var i = 0, l = arguments.length; i < l; i++)
                result += arguments[i] + " ("+typeof arguments[i]+") ";

            alert(result);

        }
    },
    log: function() {
        this.method = "log";
        this._consoleMethod.apply(this, arguments);
    },
    info: function() {
        this.method = "info";
        this._consoleMethod.apply(this, arguments);
    },
    warn: function() {
        this.method = "warn";
        this._consoleMethod.apply(this, arguments);
    },
    clear: function() {
        this.method = "clear";
        this._consoleMethod.apply(this);
    },
    count: function() {
        this.method = "count";
        this._consoleMethod.apply(this, arguments);
    },
    trace: function() {
        this.method = "trace";
        this._consoleMethod.apply(this, arguments);
    },
    assert: function() {
        this.method = "assert";
        this._consoleMethod.apply(this, arguments);
    },
    dir: function() {
        this.method = "dir";
        this._consoleMethod.apply(this, arguments);
    }
}; // end console wrapper.

// example data and object
$C.clear();

var foo = "foo", bar = document.getElementById("divImage");
$C.log(foo, bar);
$C.count("loop");
$C.info("we are inside a loop!");
$C.count("loop");
$C.assert(parseInt("07") === 7)
$C.warn("please use radix 10 when using parseInt");

// enable debugging of a function, eg, finding a hoisting bug...
var consoleTesting = function() {
    $C.info("starting function debugging");
    $C.trace();

    $C.warn("a should be undefined but it is not, actually assigned to the function already:");
    $C.info(a); // undefined?
    function a() {
        alert("hi");
    }

    var a;
    $C.log(a.toString()); // the function
}();

Here's a link to this in action on jsFiddle

The interface is simple, just use the "$C" namespace (name it what you like) instead of "console" will have the preserved functionality / results within FireFox, whereas IE users can either see an alert pop-up dialogue or nothing at all (if $C.quietDismiss = true).

// To disable the alerts in IE, do
$C.quietDimiss = true;
// To disable debug completely:
$C.debug = false;

I hope somebody finds this useful...

update history

*update* thanks to divide by zero for the improvement - in this post.

*update again* this is a fix from the previous update, seems that safari's console cannot accept scoping through apply so I added a try {} catch {} block where it reverts to the old loop

*update again (15/02/10)* modded to support any possible console method with ease

*update again (08/04/10)* changes to the firebug API have resulted in certain console methods being dropped and others changed (for example, .debug no longer sets a breakpoint), the methods that are in the current version are: log, debug, info, warn, error, exception, assert, dir, dirxml, trace, group, groupEnd, groupCollapsed, time, timeEnd, profile, profileEnd, count, clear. More here: http://getfirebug.com/wiki/index.php/Console_API

*update again (17/02/11)* fixed to console.method.apply(console, arguments); as opposed to .apply(this, arguments) which fixes webkit and removes the need for a try block / iteration fallback. added console.dir


Feb 15th 2010 × on performance of looping arrays and objects in javascript

There are probably as many ways of looping an array in javascript as there are phrases that are synonymous for masturbation. So, what are they and how fast do they perform? Head over to this test @ a blog on Sun which sports no less than 17 different loops to get a fair indication of how fast things work in today’s browsers.

Disappointingly, the native array.forEach in my firefox 2.5 seems to be one of the worst results. while(length–) { … } ftw!


Feb 14th 2010 × Detect adblocking software through mootools and have a fallback strategy

Being able to check if website adverts were blocked is of some importance. As browser plugins such as AdBlock Plus and the likes are getting better all the time, less and less of your adverts get served up. Hence, my simple solution that can try and recover some of the lost revenue.

Google adwords code can be wrapped into a plain DIV tag without a set width/height, letting any content injected into it by Google dictate its size. Through a basic Element prototype shortcut, called Element.adCheck(), you can check if the size has remained less than expected (aka, the AD got blocked).

Element.implement({
    adCheck: function(options) {
        var options = $merge({
            delay: 3000,
            message: "<center>It looks as if my Adwords did not display<br />I am just a programmer that needs to feed their hungry child and wife, please disable AdBlock or whatever clever advert blocking software you are running on my site.</center>",
            minHeight: 20,
            minWidth: 20
        }, options);

        this.store("timer", (function() {
            var coords = this.getCoordinates();
            if (coords.height.toInt() < options.minHeight || coords.width < options.minWidth) {
                this.set("html", options.message).fade(0, 1)
            }
        }).delay(options.delay, this));
        return this;
    }
});

// check it for a div with id="as":
$("as").adCheck();

// check it for all divs with class 'myads'
$$("div.myads").adCheck({message: 'please disable adblock'});

// use alternative adverts instead
$("as").adCheck({message:"<a href='/signup.php'><img src='/img/signupbanner.gif'>"});

To run this, you need mootools 1.2+ core only. You could also extend the above code to .pass() a function callback so you can execute a proper ‘onAdvertFailed’ event.

Just a thought, but wouldn’t it be nicer if AdSense could serve adverts through JSONP that you can parse and embed clientside? It won’t be mainstream but it could really screw with current AdBlocking methods…


Feb 9th 2010 × interviewing for javascript developers and advanced javascript tests

We’re shortly due to hire a new lamp/javascript developer at work and I just came across a very interesting post by Nicholas C. Zakas of Yahoo and O’Reilly fame. Although his requirements are probably a bit excessive (considering the enterprise level he’s at), I think there are some good ideas – definitely worth a read if you ever want to partake in the ‘big time’. Link here. Another interesting post explaining this javascript test offers some great insights into the workings of ECMA / javascript.

If you find the test by Dmitry Baranovsky easy to cope with, have a go at this one by Juriy Zaytsev of Prototype core dev team, some nasties there but it’s laced with knowledge of the inner workings of javascript. Is it fair to expect somebody you are interviewing to solve these, that’s a different subject…