Beautiful JS

JavaScript that belies the common conception of beauty

Underscore's pluck

Underscore has taken a common use-case of the conventional map method and made it a method of its own, pluck:

_.pluck = function(obj, key) {
  return _.map(obj, function(value){ return value[key]; });
};

It takes an array or object and plucks values out of its members:

var a = document.getElementsByTagName('a');
_.pluck(a, 'href'); // get all `href` attributes as an array

It’s dead simple, but proves how valuable it is to abstract based on common usage.

PS. Please check out the new jsapi.info, a site I created to explore the source of various libraries, via which I discovered pluck!

Posted by James on 02 Nov 2011 | linky

jQuery's isPlainObject

jQuery’s isPlainObject returns true if the argument is a regular object, i.e. one created via the Object constructor or object literal ({...}).

//...
isPlainObject: function( obj ) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
        return false;
    }

    try {
        // Not own constructor property must be Object
        if ( obj.constructor &&
            !hasOwn.call(obj, "constructor") &&
            !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
            return false;
        }
    } catch ( e ) {
        // IE8,9 Will throw exceptions on certain host objects #9897
        return false;
    }

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var key;
    for ( key in obj ) {}

    return key === undefined || hasOwn.call( obj, key );
}, //...

This is nice to know too:

// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
Posted by James on 07 Oct 2011 | linky

DOM CSS normalization

I think this DOM “hack” epitomises the idea of “beauty in function” quite well:

// `props` is a hash of animate'able CSS props,
// like "width", "padding", "borderLeftWidth", "color" etc.

function normalize(style){
    var css, rules = {}, i = props.length, v;
    parseEl.innerHTML = '<div style="'+style+'"></div>';
    css = parseEl.childNodes[0].style;
    while(i--) if(v = css[props[i]]) rules[props[i]] = parse(v);
    return rules;
} 

It’s not pretty code, but what it does is quite clever. It takes a style string, such as border:1px solid #f00;width:200px; and noramlizes it into its more specific properties, but only those properties that can be animated. For border this would be borderLeftWidth, borderLeftColor, borderTopWidth etc.

Doing this without taking advantage of the inherent normalization that occurs within the DOM would require more code and be more liable to break or have inconsistencies between itself and the actual DOM.

The above code is from Thomas Fuchs’ emile.js. If you’re looking for a tiny and swift animation helper then emile works well. I will also put a word in for a respectable fork, which exposes a slightly better API and more readable source: ded/emile.

Posted by James on 05 Oct 2011 | linky

Looping with freedom

Loops don’t have to look like this:

for (var i = 0; i < array.length; i++) {}

… that’s how boring people write loops. “But”, you say, “That’s convention and therefore is the most readable format”.

True, but let’s be honest: variations of the above aren’t exactly challenging to grasp, and once you know a truthy expression from a falsey one, and the difference between prefix (++i) and postfix (i++) incrementers, there really is no point in holding back.

You don’t have to be entirely conventional.

for (var i = 0, l = a.length; i < l; i++); // cached length
for (var i = -1, l = a.length; ++i < l;); // bundled condition (increment+test)
for (var i = a.length; i--;); // reverse loop 
// The body of the loop makes changes that'll affect
// the evaluation of the condition. E.g. removing nodes:
while (node.firstChild) {
    node.removeChild(node.firstChild);
}
// Do something with node and its following siblings
do { } while (node = node.nextSibling);

A loop’s body is expendable too:

while (
    div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
    all[0]
);

That last one’s from here: https://gist.github.com/527683

Posted by James on 02 Oct 2011 | linky

Backbone events definition

Backbone.js allows you to define delegated-events for your views in a neat little hash in which you specify the event name, the selector (the context being the view itself) and a method-name to call on the view instance:

var FooView = Backbone.View.extend({

	events: {
	    "dblclick"                : "open",
	    "click .icon.doc"         : "select",
	    "contextmenu .icon.doc"   : "showMenu",
	    "click .show_notes"       : "toggleNotes",
	    "click .title .lock"      : "editAccessLevel",
	    "mouseover .title .date"  : "showTooltip"
	},

	//...

});

See the documentation for Backbone’s delegateEvents.

Posted by James on 01 Oct 2011 | linky

Subscribe or follow @Beautiful_JS! | Fork then send a pull request to contribute.

Disclaimer: Beautiful code is readable, maintainable and scalable. It's not just nice to look it.