Archive for the ‘JavaScript’ Category

Detect IE in JS using conditional comments

Posted in 'Code Snippets, Cool Stuff, JavaScript' by James on August 20th, 2010

I originally posted this as a gist, here. I thought it best to keep my readers informed so that’s why I’m posting it here too:

// ----------------------------------------------------------
// A short snippet for detecting versions of IE in JavaScript
// without resorting to user-agent sniffing
// ----------------------------------------------------------
// If you're not in IE (or IE version is less than 5) then:
//     ie === undefined
// If you're in IE (>=5) then you can determine which version:
//     ie === 7; // IE7
// Thus, to detect IE:
//     if (ie) {}
// And to detect the version:
//     ie === 6 // IE6
//     ie > 7 // IE8, IE9 ...
//     ie < 9 // Anything less than IE9
// ----------------------------------------------------------
 
// UPDATE: Now using Live NodeList idea from @jdalton
 
var ie = (function(){
 
    var undef,
        v = 3,
        div = document.createElement('div'),
        all = div.getElementsByTagName('i');
 
    while (
        div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
        all[0]
    );
 
    return v > 4 ? v : undef;
 
}());

http://gist.github.com/527683

Dear .NET

Posted in 'General, JavaScript' by James on July 26th, 2010

A letter to the .NET magazine

Staring keenly at a stack of magazines, I hazard a gaze towards the computer section and lay my eyes upon the newest cover of my sworn enemy, the .NET magazine. Its luring front-page convinces me that it deserves my consideration.

I buy it without looking inside. I want to reserve the surprise for later.

Now sitting in the lounge at London Gatwick Airport, I whip out the magazine and rush to a JavaScript article. I can’t wait. It’s about jQuery and I just know that they’ve gotten their act together since previous encounters. I’m sure this article will contain the best in jQuery techniques and JavaScript best practices.

Well, .NET, now I’m sitting here with the unfortunate duty to inform you of my unsurprising disappointment.

The truth is I expected nothing more from you.

Please turn to page 80 of issue 205 and pay special consideration to the following reservations of mine:

The author doesn’t mention the possibility nor the applicability of creating a jQuery plugin instead of the single globally defined function ‘drawerToggler’. What’s wrong with this? Well, the function happens to contain functionality that could be abstracted to deal with arbitrary sets of nodes and is therefore suited to jQuery’s plugin mechanism. I would understand if at least a mention surfaced, but nothing.

This is certainly not the worst of it though.

The function itself:

function drawerToggler() {
    $('#theme-drawer').slideToggle("normal", function(){
        if ($('#theme-drawer').is(":visible")) {
            $('#wrapper').css("margin-bottom", "150px");
        } else {
            $('#wrapper').css("margin-bottom", "20px");
        }
    });
}

Where it’s being called:

$(document).ready(function(){
    $('.drawer-toggler').click(function(){
        drawerToggler();
    });
});

This function is called within the click event handler for any element with a className containing drawer-toggler. Since it’s getting called on every click, and the elements selected within the drawerToggler do not change between calls, it would be logical to select and “cache” the DOM object references beforehand, and then the function could use these DOM references instead of continually (and redundantly) re-querying the DOM on every single call!

The function itself contains some redundant code too. The ‘theme-wrapper’ to which the slideToggle method is applied, is then re-queried within the callback. Apparently unbeknown to the author, this very element can be accessed via the ‘this’ keyword within the callback function — there is absolutely no need to re-query the DOM for #theme-wrapper!

This isn’t just a case of my preferences versus the author. There are established best practices in the jQuery community and as one of the industry’s top publications you should be providing not only good advice but near enough the best advice according to current industry practices and standards.

Replacing text in the DOM… it’s not that simple!

Posted in 'JavaScript' by James on July 7th, 2010

Finding and replacing strings of text in a DOM document is very tricky, especially if you’re looking to do it properly and entirely unobtrusively.

In this context, “unobtrusive” means affecting the page in a minimally invasive manner — minimal DOM destruction, no un-called-for normalizing of text nodes (i.e. joining separate text-nodes together) etc.

Essentially, what I’m talking about is finding a specific piece of text in a page or a piece of text that matches a pattern and then doing something with it — possibly wrapping it in an element or changing it in some other way.

Imagine that we have the following paragraph:

<p>This order's reference number is RF83297.</p>

We want to locate the reference number and wrap it in an anchor that leads to the order’s page (pretend that we’re making a bookmarklet to enhance the admin panel of some online store). Given what we know about the HTML structure this can be quite easy:

(Using jQuery)

jQuery('p').each(function(){
 
    var p = $(this);
 
    p.html(
        p.text().replace(
            /\bRF\d{5}/g,
            '<a href="/order/$&">$&</a>'
        )
    )
 
});

The problem with this approach is that it breaks once you add any elements to any of the paragraphs on the page. It sets the content of the the element to the (replaced) text of that very element, by definition destroying any non-text nodes in the process, or, to put it poetically, it flattens the DOM structure of any given paragraph.

Additionally, the above technique does not care to avoid paragraphs that do not contain a match for the pattern — it’ll blindly flatten all elements within the paragraphs…

Creating fairies

Posted in 'Cool Stuff, JavaScript' by James on June 1st, 2010

I wanted to give a brief overview of the techniques involved in creating this. It’s slightly pointless but still quite fun and I think there are a few notable aspects of its implementation that are worth writing about.

It is a “Fairy” generator that creates small pulsating elements and moves them around the viewport in random directions, but not too eratically. You can load some fairies on this page by clicking this bookmarklet!

The original problem was that I had been mentally locked-in to a specific type of animation on the client-side and I would sit there thinking, “Okay, how do I animate this element from ‘a’ to ‘b’ while taking random detours and going in random directions?”… I was still in the conventional client-side animation mindset, where you take an element and call an animation function to mutate certain CSS properties over a specified duration. The prospect of doing it via manual calls to setInterval, with delta-x/y values never crossed my mind.

Eventually it became clear that this would be the only way to make the fairies fly as I wanted — i.e. not to simply go from ‘a’ to ‘b’, but to continually fly around in all directions.

I started with an environment singleton instance, named “fairyEnvironment”, containing the various bounds and rules attributed to the current document:

var fairyEnvironment = new function() {
 
    this.bounds = {
        left: 0,
        top: 0,
        right: 0,
        bottom: 0
    };
 
    this.speedLimit = {
        high: 6,
        low: 2
    };
 
    this.spawn = function(amount, postCreation, config) {
 
        var ret = [], i = -1;
 
        amount = ~~amount
 
        while (++i < amount) {
            postCreation &&
                postCreation.call(
                    ret[i] = new fairyEnvironment.Fairy(config)
                );
        }
 
        return ret;
 
    };
 
};

The generated fairies would have to stay within a specific area, defined by the bounds, “top”, “left”, “right” and “bottom”. I also specified the upper and lower speed limit and a function to spawn multiple Fairy instances at once. You can see that I’ve referenced fairyEnvironment.Fairy, which is defined as follows:

Questioning non UI events

Posted in 'General, JavaScript' by James on April 29th, 2010

Without UI events we’d be totally lost on the client-side — it is only via User Interface Events that we can know what the user wants to do. User-initiated events such as “click”, “mouseover” and “mousemove” are absolutely essential to upholding a user-centred experience and I want to make it clear that my quarrel is not with these types of events. Any event that gives us information about users’ intentions is a good event.

I feel that there are bad events too though — events that we feel we must utilise just because they exist — and we feel we must build into our APIs just because event-driven design dictates absolute submission to this way of thinking.

DOM mutation events

Every single time I’ve ever considered using a DOM-mutation event I’ve subsequently discovered that the problem can be better solved by re-architecting the codebase. The DOM2 mutation events aren’t well supported in less capable browsers but offer an interesting opportunity — to listen for document mutation such as attribute changes, node insertion and node removal.

This sounds cool and I’m sure your mind was buzzing with ideas when you first heard about these events, but have you actually encountered a situation where utilising these events is the only viable solution? If so, I’d love to hear it.

Another JavaScript quiz

Posted in 'JavaScript' by James on April 27th, 2010

After attempting multiple JavaScript quizzes I thought it would be fun to have a go creating one for my readers. It doesn’t involve any DOM nonsense or browser quirks — this is just plain ECMAScript (3rd edition). It tests your knowledge of the language and specification.

Within each textbox enter what you think the piece of code above it returns.

  • The pieces of code are executed as statements.
  • Enter your answer as if you were typing the code literally. So, if the answer is the string "foo", then type "foo" (including the quotes — double or single), and if it’s an array like [1,2,3] then type it out just like that.
  • If you think an error is thrown then enter ERROR in the appropriate textbox.
  • When there are multiple statements, type what the last statement returns.

Please access this post directly in order to take the quiz (note: JavaScript must be enabled).

Library foundation code

Posted in 'Code Snippets, JavaScript' by James on April 26th, 2010

It should now be universally accepted that extending the DOM directly (via Element.prototype, Node.prototype etc.) is a bad idea. To combat the inherent problems with doing this, quite a few libraries (jQuery, BBC Glow, etc.) employ the “wrapper” technique which involves taking a bunch of DOM elements and stuffing them into an array-like object that inherits from a safely-extendible prototype.

If you want to have a go at creating your own jQuery-like DOM library, here’s something to get you started:

window.myWrapper = (function(){
 
    // "cache" useful methods, so they don't
    // have to be resolved every time they're used
    var push = Array.prototype.push,
        slice = Array.prototype.slice,
        toString = Object.prototype.toString,
 
        isArray = function(o) {
            toString.call(o) === '[object Array]';
        },
 
        toArray = (function(){
 
            try {
 
                // Return a basic slice() if the environment
                // is okay with converting NodeLists to
                // arrays using slice()
 
                slice.call(document.childNodes);
 
                return function(arrayLike) {
                    return slice.call(arrayLike);
                };
 
            } catch(e) {}
 
            // Otherwise return the slower approach
 
            return function(arrayLike) {
 
                var ret = [], i = -1, len = arrayLike.length;
 
                while (++i < len) {
                    ret[i] = arrayLike[i];
                }
 
                return ret;
 
            };
 
        })();
 
    function NodeList(elems) {
 
        this.length = 0;
 
        push.apply(
            this,
            elems.nodeType ?
                [elems] // Single node
                : isArray(elems) ?
                    elems // Real array
                    : toArray(elems) // NodeList/Array-like-object
        );
 
    }
 
    function myWrapper(elems) {
        // Instantiate and return new NodeList
        return new NodeList(elems);
    }
 
    myWrapper.NodeList = NodeList;
 
    NodeList.prototype = {
 
        each: function(fn) {
 
            for (var i = -1, l = this.length; ++i < l;) {
                fn.call(this[i], this[i], i, l, this);
            }
 
            return this;
 
        }
 
    };
 
    return myWrapper;
 
})();

There’s no selector engine, but it’s easy enough to slot one in:

function myWrapper(elems) {
    // E.g. using Sizzle as the selector engine
    return new NodeList( typeof elems === 'string' ? Sizzle(elems) : elems );
}

Example of usage:

myWrapper(document.getElementsByTagName('div')).each(function(){
    alert(this.id);
});
// Etc.

Extending NodeList:

myWrapper.NodeList.prototype.applyCSS = (function(){
 
    var hasOwn = Object.prototype.hasOwnProperty;
 
    return function(props) {
 
        for (var i = -1, l = this.length; ++i < l;) {
            for (var p in props) {
                if (hasOwn.call(props, p)) {
                    this[i].style[p] = props[p];
                }
            }
        }
 
        return this;
 
    };
 
}());
 
// Usage:
myWrapper(document.body).applyCSS({
    background: 'red',
    color: 'yellow'
});

76 bytes for faster jQuery

Posted in 'JavaScript' by James on March 31st, 2010

When jQuery fires a callback function, whether it is an event handler, an each iterator, or a filter function, it will normally give you a DOM element as the function’s context, accessible via the this keyword. It’s common practice to subsequently wrap this in jQuery(...) resulting in a newly constructed jQuery instance.

This is no fault of jQuery’s but this practice of redundant “wrapping” generally sucks.

I “tweeted” a while ago, complaining of this:

Constructing a new jq obj on each iteration just to access some text seems wrong. jQuery(‘a’).map(function(){ return $(this).text(); });

Paul Irish suggested that I use jQuery.text instead of jQuery.fn.text, meaning that I wouldn’t have to bother with constructing a new jQuery object every single time my function was called. This was a good suggestion but unfortunately not all of jQuery’s methods have corresponding single-node functions, and it would mean not being able to chain methods.

This is a growing problem, and is only worsened by developers’ insistence on constructing multiple jQuery objects with the same element! -

jQuery('a').click(function(){
 
    if (jQuery(this).attr('href') === 'blah') {
        jQuery(this).next('.whatever').slideToggle();
    }
 
    jQuery(this).fadeTo('slow', 0.4);
 
    return false;
 
});

Eew! Not only are there multiple pointless constructors, but Mr. Uber Cool jQuery Developer isn’t accustomed to the DOM, so has absolutely no idea that, in most situations, this.href would suffice for getting the href property value. This kind of misuse has been discussed in step #3 here: http://encosia.com/2010/03/30/5-steps-toward-jquery-mastery/.

The real problem remains that there are three jQuery objects being constructed, — I feel that it is a library’s obligation to protect against misuse like this. Even if you’re only constructing one jQuery object in a callback it’s still somewhat pointless, in that you’re only doing so to call a couple of methods…

Labelled blocks, useful?

Posted in 'JavaScript' by James on March 23rd, 2010

I was recently playing around with labels and blocks (as one does) and found that they could be used as a way to annotate/contain chunks of JavaScript without using ugly comments (or abstraction?). I can see its appeal – it offers a sense of containment that is hard to get with just comments.

I’ll use the example of a really simple “getJSONP” function:

// BEFORE
function getJSONP(url, success) {
 
    var ud = '_' + +new Date,
        script = document.createElement('script'),
        head = document.head || document.getElementsByTagName('head')[0]
                             || document.documentElement;
 
    window[ud] = function(data) {
        head.removeChild(script);
        delete window[ud];
        success && success(data);
    };
 
    script.src = url.replace('callback=?', 'callback=' + ud);
    head.appendChild(script);
 
}
 
// AFTER
function getJSONP(url, success) {
 
    declareVars: {
        var ud = '_' + +new Date,
            script = document.createElement('script'),
            head = document.head || document.getElementsByTagName('head')[0]
                                 || document.documentElement;
    }
 
    defineCallback: {
        window[ud] = function(data) {
            head.removeChild(script);
            delete window[ud];
            success && success(data);
        };
    }
 
    addScriptToDocument: {   
        script.src = url.replace('callback=?', 'callback=' + ud);
        head.appendChild(script);
    }
 
}

You may think that doing this borders on totally pointless and in this instance I’d probably agree, but you must admit it could potentially be useful in other situations (none come to mind though).

We’re normally used to { and } being used to delimit an object literal, but they can also be used to create blocks (or “compound statements”). A compound statement is a statement that contains any number of sub-statements. We use them all the time:

while (true) normalStatement;
while (true) {
    iAmInACompoundStatement;
    soAmI;
    ermICallItABlock;
}

We use them everywhere, if statements, for statements, while statements, switch statements, try/catch statements …

Sorting elements with jQuery

Posted in 'Code Snippets, JavaScript' by James on March 19th, 2010

There are quite a few table-sorting functions/plugins out there, but I’ve yet to find something sufficiently low-level for general element-sorting. What I wanted was a simple jQuery plugin that would take a sorting function (just like Array.prototype.sort) as an argument and sort the DOM elements in-place, and would handle situations where the elements didn’t all have the same parent.

To be honest, I didn’t search for very long — I prefer making this kind of stuff myself anyway — so, I present what I think is one of the simplest ways possible to implement a reliable element-sorting function.

Name changed from sort to sortElements as per David’s comment.

/**
 * jQuery.fn.sortElements
 * --------------
 * @param Function comparator:
 *   Exactly the same behaviour as [1,2,3].sort(comparator)
 *   
 * @param Function getSortable
 *   A function that should return the element that is
 *   to be sorted. The comparator will run on the
 *   current collection, but you may want the actual
 *   resulting sort to occur on a parent or another
 *   associated element.
 *   
 *   E.g. $('td').sortElements(comparator, function(){
 *      return this.parentNode; 
 *   })
 *   
 *   The <td>'s parent (<tr>) will be sorted instead
 *   of the <td> itself.
 */
jQuery.fn.sortElements = (function(){
 
    var sort = [].sort;
 
    return function(comparator, getSortable) {
 
        getSortable = getSortable || function(){return this;};
 
        var placements = this.map(function(){
 
            var sortElement = getSortable.call(this),
                parentNode = sortElement.parentNode,
 
                // Since the element itself will change position, we have
                // to have some way of storing its original position in
                // the DOM. The easiest way is to have a 'flag' node:
                nextSibling = parentNode.insertBefore(
                    document.createTextNode(''),
                    sortElement.nextSibling
                );
 
            return function() {
 
                if (parentNode === this) {
                    throw new Error(
                        "You can't sort elements if any one is a descendant of another."
                    );
                }
 
                // Insert before flag:
                parentNode.insertBefore(this, nextSibling);
                // Remove flag:
                parentNode.removeChild(nextSibling);
 
            };
 
        });
 
        return sort.call(this, comparator).each(function(i){
            placements[i].call(getSortable.call(this));
        });
 
    };
 
})();

Features:

  • It’s low-level — you have control over how it sorts (see the “comparator” argument).
  • It lets you sort any arbitrary DOM nodes, as long as none of those nodes are within another one of the nodes to be sorted.
  • It lets you specify what elements will actually be moved (see the “getSortable” argument).

Usage:

Assuming the following markup:

<ul>
    <li>Banana</li>
    <li>Carrot</li>
    <li>Apple</li>
</ul>

You could sort the items alphabetically like so:

$('li').sortElements(function(a, b){
    return $(a).text() > $(b).text() ? 1 : -1;
});

That would result in:

<ul>
    <li>Apple</li>
    <li>Banana</li>
    <li>Carrot</li>
</ul>

See a rather basic demo here.

jQuery.fn.sortElements on Github