Follow me: @D_mitar

Most read posts recently



May 7th 2010 × mooPlaceholder: input placeholder behaviour class

Some of the HTML5 features just won’t come fast enough. One such feature is the ability to apply a placeholder input attribute, something like placeholder=’your text’ which takes the text and sets it as a pseudo default input value until it’s focused or changed. Currently (at time of writing), this only works in webkit-based browsers like Chrome and Safari but it’s not difficult to bring it into all other browsers.

Here’s a little class / mootools plugin that I wrote to do it:

var mooPlaceholder = new Class({
    // behaviour for default values of inputs class
    Implements: [Options],

    options: {
        // default options
        htmlPlaceholder: "placeholder", // the element attribute, eg, data-placeholder="MM/YY" -> "data-placeholder"
        unmoddedClass: "unchanged", // apply a class to the unmodded input, say, to grey it out
        parentNode: document, // limit to a particular set of child nodes
        defaultSelector: "input[placeholder]"
    },

    initialize: function(options) {
        this.setOptions(options);
        // detect native support (nothing gets done if true)
        this.nativeSupport = 'placeholder' in document.createElement('input');
    },

    attachToElements: function(selector) {
        // basic function example that uses a class selector to
        var inputs = this.options.parentNode.getElements(selector || this.options.defaultSelector);

        if (inputs.length) {
            inputs.each(function(el) {
                this.attachEvents(el);
            }, this);
        }
    }, // end attachToElements

    attachEvents: function(el, placeholder) {
        // method that attaches the events to an input element.
        var placeholder = placeholder || el.get(this.options.htmlPlaceholder);
        if (this.nativeSupport || !$(el) || !placeholder || !placeholder.length)
            return;

        el.set("value", placeholder).store("placeholder", placeholder);

        // append unmodded class to input at start
        if (this.options.unmoddedClass)
            el.addClass(this.options.unmoddedClass);

        // now cater for the events
        el.addEvents({
            change: function() {
                // when value changes
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value != placeholder) {
                    // once it changes, remove this check and remove the unmoddedClass
                    el.removeClass(this.options.unmoddedClass).removeEvents("change");
                }
            }.bind(this),

            focus: function() {
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value == placeholder) {
                    el.set("value", "").removeClass(this.options.unmoddedClass);
                }
            }.bind(this),
            blur: function() {
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value == placeholder || value == "") {
                    el.set("value", placeholder).addClass(this.options.unmoddedClass);
                }
            }.bind(this)
        });
    }
});

// example use for a single element:
new mooPlaceholder().attachEvents($("searchfield"), "type here to search site..."); // override placeholder

new mooPlaceholder().attachEvents($("searchfield")); // use placeholder= value instead

// example use for any input element that has a placeholder property:
new mooPlaceholder().attachToElements(); // defaults to "input[placeholder]"

// or a custom selector
new mooPlaceholder().attachToElements("input.foo");

// or only inputs of a particular form with a particular class (required)
new mooPlaceholder({
    parentNode: $("mysignupform"),
}).attachToElements("input.required[placeholder]");

Clearly, many ways to use it. It will only work as progressive enhancement and won’t attach anything if the browser already supports placeholders.

Make sue you set the CSS class for the unmodded input (look at the CSS tab on JSFIDDLE for an example) – it seems that natively, the text goes to #A9A9A9 but you may be able to override that. Vendor specific CSS styles allow you to work with this:

input.unmodded::-webkit-input-placeholder {
    color: rgb(100, 0, 0);
}

Have fun.


Apr 28th 2010 × mootools 1.3 beta 1 is finally out for grabs

In what promises to be the most significant update thus far (well, perhaps the mootools 1.11 to 1.2 is still worth mentioning as a milestone), the mootools team have finally released a public beta.

Why is it significant? You can read the blog post but here is the short summary: in addition to being set as the groundwork for the transition to mootools 2.0, here are the most significant changes I have found out about (list will expand as I find out more):

  • The introduction of slick (new css pseudo selector engine) – awesome news for DOM manipulation speeds
  • Move away from `Native` to `Type` (semantics) – internal change mostly, unless you hack into Natives
  • Move away from $names – all things like $chk, $type, $A and so forth are now being renamed to plain function names that make more sense and look more javascript-y.
  • Move away from feature detection where possible and a larger reliance on UA parsing instead – not sure I appreciate this as I do not trust UA strings at all, not least of all due to the fact that I often browse with the googlebot UA string when chasing SEO issues around our competitors. There was a nasty post by slicknet a while back in which he ripped into mootools for still using feature detection but I fail to see his point (just as many others do). Both are somewhat unreliable but one does not allow for the user to directly interfere with the framework behaviour.

Additionally, it seems as if the framework has been largely rewritten – most of the source code is totally new to me and it will take me some time to get a better understanding on how it works. Thus far, there’s been no mootools-more 1.3 so I suppose Aaron Newton will be working hard on releasing it soon.

The new documentation is live (courtesy of fakedarren) here: http://mootools.fakedarren.com/docs/core

P.S. Oskar reported earlier via twitter that JS Fiddle has updated and included the beta as framework option so you can play with it.

The


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!