Coding with Jesse

Detect Internet Explorer 6 in JavaScript

Update: As some of the comments mention, the technique below doesn't work anymore. It's best to use object detection to accomplish what you need, or use conditional comments. But if you need to detect IE6, this should work: /MSIE 6/i.test(navigator.userAgent)

Sometimes you just have to sniff for Internet Explorer 6 (and under) in JavaScript. Using conditional comments is a decent solution, but I don't want them scattered all over my code.

With a bit of help from Dean Edwards, I worked out the following:


var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;

With just a single conditional comment, you can have a JavaScript variable that you can reuse throughout your code.

(IE6 will be true in Internet Explorer 6 and under, but does anyone really care about IE5 anymore? Thought not.)

You could use this technique to sniff for other things:


// exactly Internet Explorer 7
var IE7 = false /*@cc_on || @_jscript_version == 5.7 @*/;

// at least Internet Explorer 7
var gteIE7 = false /*@cc_on || @_jscript_version >= 5.7 @*/;


// any Internet Explorer (thanks to Dean)
var isMSIE = /*@cc_on!@*/false;

Note: browser sniffing is evil but sometimes painfully necessary.

Published on September 21st, 2007. © Jesse Skinner

jQuery 1.2 Notes

Great news: jQuery 1.2 is out, and the heavily-awaited jQuery UI (an "official" replacement to Interface) will be out on Sunday.

Be sure to have a read of the jQuery 1.2 release notes, but here are a few highlights that I can't wait to use:

  • Partial .load()

    If you want to load a whole web page with Ajax, but only want to cut out a portion of the page and stick it in to an element on your page, you can now do this:

    $('#stats').load('same_page.php #stats');
    

    This will make it really easy to refresh an element on the page without needing to change the server-side logic.

  • New Traversing Features

    Traversing and selecting remain the most powerful features in jQuery, and it'll be great to be able to use .andSelf() to include the original set when traversing, and .prevAll()/.nextAll() to select all the siblings to one side or the other.

  • New Effect Features

    There are a ton of new Effects features, but my favourite is probably .stop(), allowing us to finally stop animations. Together with the new :animated selector, it's trivial to stop all ongoing animations at once:

    $(':animated').stop();
    

    Some of the other Effects features have a lot of potential, like being able to use percents or animating by a relative amount (eg. add 100px).

    Extremely powerful is the new ability to write extensible animations. I assume they did this to ease in the development of jQuery UI, but this allows all of us to harness the power of jQuery animations to do anything we want! Okay I'm starting to drool...

Oh, one more thing. If you're used to creating elements in jQuery like this:

var link = $('<a>');

then you need to stop using invalid HTML and either make it XHTML compliant or add a closing tag, otherwise it will break in Internet Explorer. This is true since jQuery version 1.1.4, and I've had to make a lot of changes in my code as a result.

Now any of the following is okay:

// okay, XHTML-style
var xhtmllink = $('<a/>');

// also okay, valid HTML style
var htmllink = $('<a></a>');
Published on September 11st, 2007. © Jesse Skinner

Avoiding web page zoom in the iPhone and iPod Touch

I've been doing a bit of experimentation with web pages in the iPhone, and I've realised the first major problem is trying to prevent the iPhone from zooming way out on a web page, making it rather difficult to work with and more difficult to read.

One would assume that a 100%-width layout would be good enough for the iPhone's Safari to stop setting the browser width to 964px. But alas, it's not.

I had a look at probably the best iPhone app out there right now, Joe Hewitt's Facebook for the iPhone. Joe was awesome enough to release iUI, a collection of CSS, JavaScript and images for making user interfaces that match the look and feel of the iPhone.

I spent some time dissecting his examples, and eventually realised it wasn't the CSS nor the JavaScript which was able to stop the iPhone Safari from resizing. It was just a <meta> tag in the <head>:

<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>

It turns out this was all well documented on Apple's site, iPhone for Web Developers. There are lots of other techniques discussed like how to specify stylesheets and integrate with the built-in Google Maps application.

I hope that other device manufacturers implement things like the <meta name="viewport"> tag and the only-for-certain-widths stylesheets. The iPhone may not end up in everyone's pocket, but we will need to make web sites and applications that work across an ever-wider range of devices.

Published on September 7th, 2007. © Jesse Skinner

Private JavaScript Variables

I find myself needing to generate unique IDs in JavaScript a lot lately. Mostly this happens when I'm creating a lot of elements dynamically and I need to assign some unique ID to them so that I can find them later. And I need to do this because I've realised that storing pointers to elements in JavaScript uses a LOT more memory than just storing the ID of an element and finding it later with getElementById().

So anyway, what's a good way of generating unique IDs?

A simple way is to just keep a counter going and increment it every time you access it, like this:

var guid_counter = 0;

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID and increment counter
    div.id = 'div_' + (guid_counter++);

    // append to the page
    document.body.appendChild(div);
}

With this you'll end up with 100 <div>s with IDs from "div_0" to "div_99". But our guid_counter is just sitting out there in the open! Someone could come along (like us making a typo) and write guid_counter = 0 or guid_counter-- and mess everything up! We could end up with 2 elements with the same ID!

We can improve on this by using a private variable. Private variables give us more control over global variables like our guid_counter, because we can choose when and how they are accessed and updated. Let's create a function which contains our guid counter as a private variable:

// 'guid' is assigned to the return value of this outer function
var guid = (function() {
    // same guid counter, but we keep it hidden inside here
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // whenever guid() is called, return and increment the counter
        return guid_counter++;
    };

})(); // note the () - this executes the outer function right now!

If this is the first time you're seeing this syntax (or the hundredth) it can be a bit confusing. Really, we're doing the same thing as this:

function generate_guid_function() {
    // nothing outside of generate_guid_function() can access this
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // return and increment the counter
        return guid_counter++;
    };
}

// 'guid' is assigned to return value of generate_guid_function()
var guid = generate_guid_function();

The magic of all this is in JavaScript Closures. The inner function has access to guid_counter because of where it's defined, but our code outside has no way to change or access guid_counter. This is exactly like private instance variables in Java and other languages.

Now, we can safely rely on our guid() function to generate unique IDs without worrying about our guid_counter being touched:

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID from our new guid() function
    div.id = 'div_' + guid();

    // append to the page
    document.body.appendChild(div);
}
Published on August 26th, 2007. © Jesse Skinner

addDOMLoadEvent Revisited

I've gone back and reworked addDOMLoadEvent. I got rid of the global variables and reduced the size down to 563 bytes!

For the new script and the demo pages, check out addDOMLoadEvent.

Update [Aug. 19, 2007]: Now the script preserves any existing window.onload function, and also executes functions instantly when called after the page has already loaded. But now the compressed version is a hefty 617 bytes.

Published on August 14th, 2007. © Jesse Skinner
<< older posts newer posts >> All posts