“prettyPrint” for JavaScript

Posted in 'JavaScript' by James on June 5th, 2009
“prettyPrint” for JavaScript

Those of you following me on Github may have noticed a recently added project called “prettyPrint“.

“prettyPrint” is an in-browser JavaScript “variable dumper” similar to ColdFusions’s cfdump. It enables you to print out an object of any type in table format for viewing during debugging sessions. In combination with Firebug, “prettyPrint” will make you the best-equipped JavaScript debugger on earth! (not guaranteed)

Some of its key features:

  • Entirely independent. It requires NO StyleSheets or images.
  • Handles infinitely nested objects.
  • All native JavaScript types are supported plus DOM nodes/elements!
  • Protects against circular/repeated references.
  • Allows you to specify the depth to which the object will be printed.
  • Better browser users benefit from gradient column-headers! Thanks to HTML5 and CANVAS!
  • Allows low-level CSS customisation (if such a thing exists).
  • Fully validated with JSLint!

I’ve recorded a short screencast demonstrating a couple of prettyPrint’s features:

“prettyPrint” is entirely independent. As mentioned, it requires NO StyleSheets or images. All styles are applied directly (inline) and are customizable via the exposed config object (prettPrint.config.styles).

See the demonstration »

Using it is simple enough:

var table = prettyPrint( anyRandomThing, { /*optional options object */ } );
 
// Making the table viewable is down to you...
// e.g.
document.body.appendChild(table);

For more information visit the project’s page on Github!

PS. I’m aware that there’s an already existing “port” of ColdFusion’s cfdump - I only created my one as a personal exercise; plus none of the ones I’ve seen so far have depth control or protection against circular references. ;)

Rant: “Crappy comments”

Posted in 'General' by James on June 4th, 2009
Rant: “Crappy comments”

Okay, this really annoys me sometimes; you read a blog post or article, it was interesting, you scroll to the comments section looking for an interesting discussion regarding the content of the post but all you find is countless grammatical or technical corrections contributed by “helpful” readers!

For me, the comments section of a blog post is where users can contribute thoughts concerning the material covered in the post; not spelling mistakes, or a complaint about the site’s content. Apparently not everyone feels this way, because whenever I go to read the comments there’s always a few egotistical children leaving comments about the damn grammar!

Now, I’ve probably done it a couple of times in the past myself but I’ve stopped - I eventually realised I was only doing it to gratify my own needs; I wasn’t really interested in the grammatical correctness of any post - my ego just required a frequent pampering, apparently in the form of grammatical superiority.

Sometimes it may be appropriate to point out these mistakes; if the mistake will cause obvious grief to other readers who take heed from the post then by all means contribute your correction, but if it’s a tiny, barely-noticeable mistake then please keep it to yourself… or, if you feel your alter-ego “Grammar Nazi” kicking in then, by all means, send the owner of the site an angry email.

I have a secret; I delete non-spam comments sometimes! Yes, it’s true! Do you know why? Because this is my website and as such I feel it my responsibility to police the content of it - if a comment is defamatory or just downright rude then I’ll delete it, or if I feel the comment adds absolutely nothing to the post then I’ll delete it. For example, if your comment contains just one word like “Wow!” or “First!” or “Interesting!” then it’ll probably be deleted; why are you wasting your time writing such drivel?

I rarely have a problem though; this is a tiny blog with a tiny readership; something I’ve come to appreciate greatly!

I do feel sorry for the guys at Smashing Magazine! With over 100,000 subscribers they really do get some crap appearing in their comments!

Fastest way to build an HTML string

Posted in 'Code Snippets, JavaScript' by James on May 29th, 2009

You have a massive array of items that needs to be transformed into an HTML list without causing the user any grief. There are a couple common ways of doing this:

Note: I’m not even going to bother covering direct DOM manipulation for every list item because that’d be plain stupid!

#1 - Step-by-step string concat

Not the slowest method that exists but probably the most common:

var arr = ['item 1', 'item 2', 'item 3', ...],
    list = '';
 
for (var i = 0, l = arr.length; i < l; i++) {
    list += '<li>' + arr[i] + '</li>';
}
 
list = '<ul>' + list + '</ul>';

Don’t use this. It’s inefficient, slow and ugly!

#2 - Step-by-step array pushing

var arr = ['item 1', 'item 2', 'item 3', ...],
    list = [];
 
for (var i = 0, l = arr.length; i < l; i++) {
    list[list.length] = '<li>' + arr[i] + '</li>';
}
 
list = '<ul>' + list.join('') + '</ul>';

Slightly faster than string concatenation but still not perfect…

#3 - The holy grail!

var arr = ['item 1', 'item 2', 'item 3', ...];
 
var list = '<ul><li>' + arr.join('</li><li>') + '</li></ul>';

I won’t bore you with benchmarks; you’ll just have to believe me (or test for yourself) - this is by far the fastest method!

Using native methods (like join()), regardless of what’s going on behind the abstraction layer, is usually much faster than any non-native alternative.

Browser benchmarks

Due to a request and my own curiousity I’ve conducted a quick benchmarking test. Each method was tested with a 130-item array, the length of each item varied so any optimizations on the browser’s part would not be effective. Each method was tested 1000 times; the results below show how long each browser took to complete each method 1000 times:

“String concat” (ms) “Array pushing” (ms) “Native join()” (ms)
Firefox 3 147 148 65
Opera 9 172 125 78
IE 7 500 2297 79
Chrome 2 63 88 72
Safari 4b 146 141 60
Averages 205 559 70

Surprisingly, string concatenation came out much faster than “array pushing” on average. It seems IE7 optimizes for lengthy string operations but doesn’t care much for arrays. Also, Google Chrome performed oddly; the “string concatenation” method was quicker than the “native join()”… I’m not too sure what’s going on there! Anyway, the results were mostly predictable, and, of course, “native join()” came out on top!

Table rows as clickable anchors

Posted in 'JavaScript, Usability' by James on May 25th, 2009
Table rows as clickable anchors

Recently a question was posed on StackOverflow.com that sparked my interest. You can head over there to read it yourself. The original poster wanted to know how to make an entire table row (<tr>) clickable, like a link. This is fairly easy if you’re only after the very redimentary link functionality: it only requires you to register a click handler which will change the location to anything you desire.

An example of this:

$('tr').bind('click', function(){
    window.location = 'http://somelocation.com';
    // Or, we can grab the HREF from the first anchor:
    // window.location = $('a:first', this).attr('href');
});

This might be deemed suitable by most but, truth be told, it’s an almost pointless enhancement. The same is true for any attempted link simulations. For example, having a DIV element and making it re-locate the page upon a click. Doing this is pointless because it’s not a real link! Browsers offer a bunch of behavioural enhancements to anchors, for example:

  • Middle-click: Page opens in new tab.
  • CTRL/SHIFT click: Page opens in new window/tab.
  • Right-click options: “open in new window”, “copy link location” etc.
  • Status message is changed to the HREF of the anchor.
  • etc…

It’s impossible to simulate all of the above. Users are left confused and sometimes annoyed when an element doesn’t act like it’s supposed to…

There are no rules that state that table rows or DIV elements cannot be clickable but if you’re going to do so then it’s important to preserve what functionality is expected from anchors. Of course, most users don’t think of them as “anchors”; they’re just links to other pages, but even so, users have a set of unwritten expectations which dictate how websites should behave.

In other words, if you’re going to make an element function like an anchor then it must be an entirely accurate emulation.

Removing comments in JavaScript

Posted in 'JavaScript' by James on May 24th, 2009
Removing comments in JavaScript

For the recently developed debug.js (view) I had to come up with a way to remove all comments from any piece of JavaScript code.

I originally thought that this would be a piece of cake; a simple regex takes care of everything!

code.replace(/\/\*.+?\*\/|\/\/.*(?=[\n\r])/g, '');

This regular expression would have worked in 90% of situations but, unfortunately I had to build something that would work in every single situation.

It’s worth mentioning exactly when the above regular expression would fail:

  • When comment notation exists in a string, e.g.
  • var str = " /* not a real comment */ ";
  • When comment notation exists in a literal regular expression, e.g.
  • var regex = /\/*.*/;
  • When conditional compilation (supported in IE > 4) exists in the code, e.g.
  • /*@cc_on @*/
    /*@if (@_jscript_version == 4)
    alert("JavaScript version 4");
    @else @*/
    alert("Blah blah blah");
    /*@end @*/

While the likelihood of any of the above happening is low it’s certainly worth catering to all potential situations; just encase one of them arises!

So, after a bit of googling and messing arround, it turns out that the only way of doing this properly is to loop through the code, character by character, checking for certain delimiters and then enabling/disabling modes as the loop progresses:

Custom JavaScript with “parseScripts”

Posted in 'Cool Stuff, JavaScript' by James on May 21st, 2009
Custom JavaScript with “parseScripts”

After successfully implementing and using literalHTML (discussed here) I decided to have a go at creating a reusable basis for adding new enhancements just like it; your own “language” if you will. The code itself relies on the fact that SCRIPT elements are only run as JavaScript if their type attribute is set appropriately (or, optionally, not set at all).

So, any arbitrary content can be held in a SCRIPT element assuming the browser does not recognize its type attribute; this has been demonstrated many times. For example, John Resig takes advantage of this in his “micro-templating” example and with his popular processing.js JavaScript port.

So, as evident from Resig’s work, you can do absolutely anything you want with content held within SCRIPT tags; even port/create your own programming language!

As mentioned, I’ve been working on a simple abstraction (called “parseScripts“) to simplify the process of enhancing JavaScript as you see fit. It’s somewhat experimental at the moment; I’ve only played with it for a short while and have yet to make any firm decisions as to the true usefulness of it but I’m blogging about it for your consideration.

The extent to which this is useful is entirely dependent on your imagination. Just as an example, I’ve implemented Alex’s (from Dojo) recent Cramdas idea; usable in the following way:

<script type="application/javascript:cramdas">
var module = (#{
 
    var calc = #(n) {
        return n*n;
    };
 
    return #{
        // ... Other stuff
    };
 
})();
</script>

You should definitely have a read of Alex’s post but essentially the crux is that all occurrences of the word “function” should be replaced with a shorter syntax; he proposed what you can see above; i.e. replacing “function” with “#” (plus ‘#{}’ is equivalent to a literal function with no parameters). The usefulness of his idea is obviously debatable; I’m only using it as an example.

jQuery plugin detector

Posted in 'Code Snippets, JavaScript' by James on May 17th, 2009

This is an experimental bookmarklet which lists the jQuery plugins being used on any page. Try it out now:

Detect plugins

To save the bookmarklet simply drag the above link to your bookmarks toolbar. It’s been tested successfully in Opera 9, Safari 4, FF2/3, IE6/7, and Chrome.

The list shown includes google links for each plugin; obviously not all plugins on all sites are available to the public so the links aren’t always very useful.

I’m on Github!

Posted in 'General, JavaScript, My Life' by James on May 16th, 2009
I’m on Github!

Yes, finally I’ve joined the masses on Github and I plan to sporadically share special things on there, starting with the following recent developments:

“literalHTML”

A tiny but powerful modification allowing you to specify HTML/DOM structures inline in your JavaScript code, an example:

var className = 'active';
var myMenu = |
    <ul>
        <li class="{className}">Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
    </ul>
|;
 
// myMenu is now a DOM object:
myMenu.nodeName; // "ul"
myMenu.childNodes; // [ <li.active>, <li>, <li> ]
 
// Do something:
jQuery('body').append(myMenu);

View here »

“getDescendants”

Something I required for a project; a function to gather all descendant-elements down until a certain depth is reached:

<div>
    <ul>
        <li>Something <strong>interesting</strong></li>
    </ul>
    <p>Something <span>else</span></p>
</div>

Examples:

var div = document.getElementsByTagName('div')[0];
getDescendants(div, 1); // -> [ <ul>, <p> ]
                        // i.e. children
getDescendants(div, 2); // -> [ <ul>, <p>, <li>, <span> ]
                        // i.e. children + grandchildren
getDescendants(div, 3); // -> [ <ul>, <p>, <li>, <span>, <strong> ]
                        // i.e. children + grandchildren + great-grandchildren

View here »

I’ll hopefully be adding to the repositroy with new exciting discoveries in the near future; visit my profile and “follow me” to stay updated!

“Grayscaling” in non-IE browsers

Posted in 'CSS, JavaScript' by James on May 11th, 2009
“Grayscaling” in non-IE browsers

This started out as a little experiment and eventually turned into quite an endeavor. The task was simple enough; to emulate Internet Explorer’s ‘grayscale‘ filter in all non-IE browsers. The solution, much to my initial surprise, is not as tricky as you would think.

The ‘grayscale‘ filter in IE can be applied to any element and visually transforms the element itself into grayscale. You can apply the filter using one line of messy proprietary CSS:

elem.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)';

This can also be defined in your StyleSheet:

elem {
    filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
    /* Element must "hasLayout"! */
    zoom: 1;
}

As shown, getting this to work in IE is a piece of cake; other browsers, however, require much more attention!

There are two things to consider; images and everything else. “Everything else” is quite simple; loop through all elements within the document and look for colour properties such as ‘backgroundColor’ and ‘color’, then convert their RGB values to grayscale. There are a few ways of doing this; note that we’re not talking about desaturating a photo; “grayscaling” is slightly different (as I understand it):

Konami Craziness

Posted in 'Code Snippets, Cool Stuff, JavaScript' by James on May 9th, 2009

Just thought I’d chip in to the growing hype of adding some secret thing to your website accessible via the famous Konami cheat code. You can try it out right now:

On your keyboard press (in this order): Up, Up, Down, Down, Left, Right, Left, Right, B, A … then wait a sec.

To “benefit” from whatever happens you must be using a respectable browser (FF, Safari, IE7. Opera etc.) and, of course, you need to have JavaScript enabled. If it doesn’t work try clearing your cache first.

And with that I’m also releasing the “onKonamiCode” event handler:

function onKonamiCode(fn) {
    var codes = (function(){
            var c = [38,38,40,40,37,39,37,39,66,65];
            onKonamiCode.requireEnterKey && c.push(13);
            return c;
        })(),
        expecting = function(){
            expecting.codes = expecting.codes || Array.apply({}, codes);
            expecting.reset = function() { expecting.codes = null; };
            return expecting.codes;
        },
        handler = function(e) {
            if (expecting()[0] == (e||window.event).keyCode) {
                expecting().shift();
                if (!expecting().length) {
                    expecting.reset();
                    fn();
                }
            } else { expecting.reset(); }
        };
    window.addEventListener ?
        window.addEventListener('keydown', handler, false)
        : document.attachEvent('onkeydown', handler);
}

Use it like this:

onKonamiCode.requireEnterKey = true; // True/false
onKonamiCode(function(){
    // Do something CRAZY!
});

Have fun!