Category Archives: JavaScript

Resize iFrame to height of content, from iFrame, without an ID

There must be a more graceful way to reference the current iframe without knowing its ID?

function resizeIFrame() {
	var iframe = $('iframe',parent.document.body);
	var doc;
	var iHeightPadding;
	for (var i=0, j=iframe.length; i<j; i++) {
		if (iframe[i].contentDocument) {
			doc = iframe[i].contentDocument;
			iHeightPadding = 35;
		} else {
			doc = iframe[i].contentWindow.document;
			iHeightPadding = 0;
		}
		if (doc === document) {
			//located our iframe!
			$(iframe[i]).height(doc.body.scrollHeight + iHeightPadding);
			break;
		}
	};
}

If you have to make your own dropdown list boxes…

The dropdown list box, or select box, is a massively useful UI tool. It presents a series of predefined options in a format that doesn’t have to eat up all our screen estate. Unfortunately though, we just don’t have the ability to style this that we would like. Or, sometimes none at all. Since this is an OS control, the OS takes control – and apart from the visual implications this is no bad thing. If you absolutely have to have it match the design for the rest of your site though, you have no choice but to implement it yourself. You can achieve this either via JavaScript or Flash. Flash is probably overkill if the rest of your site doesn’t use it, so if you use JavaScript I would heavily advocate the use of progressive enhancement to start with something that works (the select tag) and build something nicer on DOM ready.

If you’re recreating something the OS does though, please try to do it as well as the OS does!

Here’s a useful list of behaviours for a dropdown list box which might seem obvious, but I have seen overlooked so many times.

  • Priority 1
    • Display the current selection in the unexpanded box
    • Display a downwards arrow to the right of the box to indicate it has a dropdown
    • When clicking the box, or the arrow, expand to offer other selections, and when clicking one of those, contract the box and display the exact text for that selection in the box
  • Priority 2
    • Allow keyboard navigation – up and down should move amongst the items, highlighting as appropriate and return should select them as if the user had clicked them
    • Allow keyboard shortcuts – pressing a character should jump the selection to items beginning with that character
  • Priority 3
    • Spacial awareness – where there is no space for a dropdown to appear beneath, or it would appear outside of the viewport, it should appear above instead
    • Dimensional awareness – where there are too many items to display reasonably a scroll bar should display to keep the entire dropdown box in view

I have seen instances where many of these have been ignored, and in the very worst case, where selecting an item showed completely different text in the contracted box. Even an instance where the text displayed actually matched a completely different item in the list!

Hope this helps. AK

JavaScript Snippets in TextMate

So, I finally started getting into snippets in TextMate. If you haven't played with TextMate – it's described by its publisher MacroMates as The Missing Editor for Mac OS X. I'd have to agree, after messing with probably 30 or more IDEs by now – it's the only one that stays simple and true and easy, until you try to do something complicated. Only then do you really realise the ridiculous amount of power under the hood. I could imagine three day long courses on this thing. You can get a 30 day trial from here, and it's only €50 to buy which is an absolute bargain in anyone's money. It's OS X only, but e-TextEditor purports to do the same thing for Windows, even supporting bundles (the plug-in format). You can read more about that here – but I can't vouch for it, having not played with it. Anyway, I digress. TextMate (and apparently e-TextEditor) supports "Snippets" – which allow you to type something short (the "Tab trigger"), press the tab key, and have it auto-complete to something more complicated. The simplest example of this – type in:

lorem

and press tab, and TextMate spits out:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Pretty cool, right? It even moves the keyboard caret focus to straight after the newly inserted text. We can go a bit further, and include "Tab stops". These tell TextMate how to behave immediately after pressing tab the first time, to auto-complete, and the following consecutive times. For an if block, for example, you probably want to start typing your condition within the brackets following the if, and then start typing inside the braces. TextMate will automatically do this for you – make sure you have a .js file open, anywhere in the document type:

if

…and press tab, and you'll get:

if (true) {};

…and "true" will be automatically highlighted. Press tab after you've typed in your conditional statement, and the caret is moved inside the braces! This is all pretty great, but there were a few snippets missing, or not working just as I would like, so I've made them myself.

To create a snippet, open up TextMate. Under the "Bundles" menu, select "Bundle Editor", then "Edit Snippets". You'll see a long list of all the code types that TextMate understands – you'll want to find and expand "JavaScript". When creating a new snippet, click the plus symbol underneath the list and choose "New Snippet". Give it a name (this doesn't affect your tab trigger, although if you choose to have a menu of potential snippets for one tab trigger, the name is what will show there. See my "Lorem Ipsum" example at the end of this post on how to do that). The code for the snippet goes in the big box (click the question mark for help with the syntax). Everything else should already be selected correctly, And you just type the text that you want TextMate to react to in the text box next to "Activation".

Firstly, I modified the existing for loop snippet, to improve performance by just checking the property we're comparing our counter against once, instead of with every iteration. This is already well documented so I won't go into it further – but here's the amended snippet:

Name: for (...) {...}
Tab trigger: for
Snippet:
for (var ${20:i}=0, ${21:j}=${1:Things}.length; ${20:i} < ${21:j}; ${20:i}++) {
${100:${1:Things}[${20:i}]}$0
};

Then, I made a snippet for the switch operator.

Name: switch
Tab trigger: switch
Snippet:
switch (${1}) {
case "${2}" :
${3}
break;
case "${4}" :
${5}
break;
default :
}

I adjusted, and possibly fixed the setTimeout snippet (I wouldn't go so far as to say it was broken – TextMate is quite mature by now, and the developers are clearly very good, but the tab stop order was confusing to me, and there seemed to be an unnecessary one in there):

Name: setTimeout function
Tab trigger: timeout
Snippet:
setTimeout(function() {$1}, ${2:10});

I created a for in loop snippet:

Name: for in
Tab trigger: forin
Snippet:
for (var ${1:key} in ${2:object}) {
${3:${2:object}[${1:key}]}
}

A try catch snippet:

Name: tray catch
Tab trigger: try
Snippet:
try {
$1
} catch(e) {
$2
}

And finally, since I was on a roll and can never remember the syntax – the ternary operator:

Name: ternary
Tab trigger: ?
Snippet:
${1:condition} ? ${2:if true} : ${3:if false}

I also wanted to improve the Lorem Ipsum snippet. The point of Lorem Ipsum text is that it represents nothing more than a blob of text – you (or, I, at least) can't read it, so you don't associate it with anything, and it is therefore perfect for layout: you see the layout, you don't read the text. But, if you keep re-inserting the exact same block of text, patterns will form in your document, and it will not have the intended effect of looking like nondescript text. www.lipsum.com have a very good Lorem Ipsum generator, that can create multiple paragaphs which do not have to begin with "Lorem ipsum". Rather than looking these up all the time, I copied few random ones, and just made a pile of snippets with the same tab trigger ("lorem") as the original one, which has the affect of showing a menu when you press tab, with all the snippets associated with that tab trigger as all your different options in the menu. I named them "Lorem ipsum 1", "Lorem ipsum 2", "Lorem ipsum 3", "Lorem ipsum (short) 1", "Lorem ipsum (long) 1", etc and applied them to "Plain Text" rather than "JavaScript".

To see all of these working, first make sure TextMate understands your file is a JavaScript file – there's a drop-down (drop-up in this instance) menu at the bottom of the TextMate window, that defaults to "Plain Text" for new files – you'll want to select "JavaScript" from this list (though the "lorem" tab trigger should work in any document). Then type any of the tab triggers we have defined (it's case sensitive) and press tab! My next task is going to be doing this for all those pesky HTML tags I can never remember the exact syntax for, and getting the try catch snippet to surround highlighted text if you have some selected.

assertArgumentType – test expected data types against actual data types

My colleague Andrew Beeching gets credit for this one. An amazingly useful piece of code to check that parameters you are receiving match your expectations. It also allows for null values to be passed, where you might be checking arguments that may or may not exist. It uses jQuery to check for parameters that might be functions and this functionality could be fallible as demonstrated in this article from DHTML Kitchen.

This is especially useful for TDD.

function assertArgumentType(expected, actual, allowNull){
        if (expected !== undefined) {
            actual = actual || null;
        }
        if (expected === Function) {
            if (!$.isFunction(actual)) {
                throw "InvalidArgNotAFunctionException";
            }
            // Match OR object is allowed to be null
        }
        else
            if ((allowNull && actual === null) || (actual.constructor && actual.constructor === expected)) {
                return;
            }
            else {
                switch (expected) {
                    case String:
                        throw ("InvalidArgNotAStringException");
                        break;
                    case Number:
                        throw ("InvalidArgNaNException");
                        break;
                    case Boolean:
                        throw ("InvalidArgNotABooleanException");
                        break;
                    case Array:
                        throw ("InvalidArgNotAnArrayException");
                        break;
                    case Object:
                        throw ("InvalidArgNotAnObjectException");
                        break;
                    case undefined:
                        throw ("InvalidArgUndefinedException");
                        break;
                    case null:
                        throw ("InvalidArgNullException");
                        break;
                }
            }
    }

jquery.metadata.js – in reverse!

Stand back, kids!

This requires jQuery, jquery.metadata.js and jquery.json.js. You will also need to modify jquery.json.js so it surrounds JSON names and values in single quotes rather than doubles, and escapes single quotes, but this is trivial.

To use:

jQuery(selector).setadata({‘name’:'value’});

It will also chain your original selection.

/******************************************************************
Name: setadata
Description: Add metadata into elements - or metadata in reverse
Author: AK
Date: 24th June 2008
Version: 0.2
Dependencies: jQuery, jquery.metadata.js, jquery.json.js
Notes:
******************************************************************/
(function($) {
    $.fn.setadata = function(jsonData) {
        return this.each(function() {
			data = '{}';
			var m = /({.*})/.exec(this.className);
            if (m) {
				data = m[1];
				this.className = $.trim(this.className.replace(data, ''));
			}
			if (data.indexOf('{') < 0) data = "{" + data + "}";
            data = eval("(" + data + ")");
			for (var property in jsonData) {
				data[property] = jsonData[property];
			}
			this.className += ' ' + $.toJSON(data);
		});
    };
})(jQuery);

Bookmarklet to de-cache CSS and images

Update: This code has been replaced.

Save the following as a bookmarklet to dynamically get the latest versions of CSS and images, regardless of caching. This version doesn’t do CSS background images, though.

javascript:(function(){var i,j,x;x=document.getElementsByTagName('link');for(i=0,j=x.length;i&lt;j;i++){x[i].href=x[i].href+'?'+new Date().getTime();}x=document.getElementsByTagName('img');for(i=0,j=x.length;i&lt;j;i++){x[i].href=x[i].href+'?'+new Date().getTime();}})();

jQuery plugin to emulate “shake” on login failure in OSX login box

Ridiculous, but still:

jQuery.fn.shake = function(intShakes /*Amount of shakes*/, intDistance /*Shake distance*/, intDuration /*Time duration*/) {
  this.each(function() {
    $(this).css({position:'relative'});
    for (var x=1; x<=intShakes; x++) {
      $(this).animate({left:(intDistance*-1)}, (((intDuration/intShakes)/4)))
      .animate({left:intDistance}, ((intDuration/intShakes)/2))
      .animate({left:0}, (((intDuration/intShakes)/4)));
    }
  });
  return this;
};
//example
$(function() {
  $('#btn').click(function() {
    $(this).shake(3, 6, 180);
  });
});