IE6 and CSS media types

When trying to reduce the number of HTTP requests on a web project I was working on, I tried to combine my screen and print stylesheets into a single file. Using the @media declaration, one should be able to specify media-specific styles. Other browsers were ok, but as usual IE6 was playing up. My @media print block was completely ignored.

The setup

First, I had only generic rules and a single print block, like so:

body { font-size: 12px }
@media print {
    body { font-size: 14pt }
}

Then, I included the stylesheet on my page with a media type:

<link rel="stylesheet" href="styles.css" media="screen, projection">

As I understood the specs all generic styles (outside the @media print block) should apply to the media specified in the link, while the print-specific styles should apply only to the print media type.

The solution

It seems the media type from the link overrides the @media print block in IE6, so removing that un-ignored those styles:

<link rel="stylesheet" href="styles.css">

Then, in order to keep my generic styles (which are actually screen-only styles) from influencing my print styles, I used another @media block:

@media screen {
    body { font-size: 12px }
}
@media print {
    body { font-size: 14pt }
}

All was well and both my screen and print styles were picked up nicely. Now I could use one single stylesheet and remove one additional HTTP request, speeding up the site loading time.

Using relatedTarget in event handlers

Here’s a nice trick for working with mouse events in a web page. Given a simple drop down menu, I used to use a timer to delay the closing of a submenu while the mouse travels from the menu title to the sub menu (where it nog longer hovers over the menu title, that had the original event handler). The timer going off would hide the submenu, unless the mouse entering the submenu fired a new event handler that would reset the timer.

So, yeah, that wasn’t ideal.

I learned the other day that, given adjacent element you can actually use event.relatedTarget to get to the element the mouse travels to when leaving the element that fired the mouseout event. Neat!

Now you can check for the target element and do all kinds of nifty stuff, and get rid of all timers. They sucked anyway.

Example jQuery code:

$('nav a').mouseout(function(e) {
    var submenu = $('.submenu', this.parentNode);
    if(e.relatedTarget != submenu) {
        submenu.slideUp();
    }
});

Javascript: typeof NodeList

Beware of Javascript’s quirky typeof:

typeof document.getElementsByTagName('p')

This will return 'function', which I did not expect. What is returned is a NodeList, which behaves like an array, identifies itself as a function, but really is neither.

If you want to detect a NodeList you’re better off with feature detection:

var isNodelist = (typeof myvar.length != 'undefined &&
  typeof myvar.item != 'undefined')

Do note that this makes it probable you’re dealing with a NodeList — but you can’t be sure.

jQuery custom selectors

I like writing jQuery plugins, so I can separate functionality into distinct units. But applying the plugin sometimes requires some logic I’d rather have in my plugin itself.

Example code

Say I want to create a plugin that creates a lightbox-style image zooming effect. I want to apply it to all links pointing at an image:

<a href="/images/photo1.jpg"><img src="/images/photo1.jpg"></a>

Here’s how I might call my awesome plugin in my main javascript file:

$(function() {
    // One option: create complex inline selectors:
    $('a[href$="jpg"], a[href$="png"]).awesome_plugin();

    // Second option: filtering
    $('a').filter(function() {
        $(this).attr('href').match(/\.(png|gif|jpe?g)$/);
    }).awesome_plugin();
});

These both might work, but they move typical plugin logic to my javascript initializer. That’s not what I want.

Custom selector

The solution is so obvious I wonder why I did not think of it before: write a custom jQuery selector:

$(function() {
    $('a:to_image').awesome_plugin();
});

Awesome: concise and with clear intent. Here’s one way to implement it:

// Somewhere in my plugin
$.expr[':'].to_image = function(obj, index, meta, stack) {
    return $(obj).attr('href').match(/\.(png|gif|jpe?g)$/);
};

Now all the logic is nicely tucked away in my plugin.

Subversion branching

The main reason why developers should use Git for versioning is cheap, cheap branching. But in Subversion it isn’t so hard that you shouldn’t use it. Here’s a basic bugfix branch workflow. First, create your branch:

svn copy /path/trunk\
         /path/branches/my-new-branch\
         -m "Create my new branch"
svn switch /path/branches/my-new-branch

Of course, /path/ is easy enough to find:

svn info | grep URL

Hack away, make some commits, and when you are ready to merge to branch back into trunk:

# Note the revision that started this branch
# assume this tells you '4362'
svn log --stop-on-copy

# Get back to trunk and merge in your changes
svn switch /path/trunk
svn merge -r 4362:HEAD /path/branches/my-new-branch

Inspect your changes, resolve conflicts and make sure everything is alright. Commit your changes…

svn commit -m "Merge in branch 'my new branch'"

…and then clean up after yourself:

svn delete /path/branches/my-new-branch\
           -m "Remove obsolete branch"

The trick is knowing where your branch started. You can note the revision number when you create the branch, or use svn log to find out.

Beware of changes to trunk before merging in your branch. If trunk has changed since you created your branch (and chances are it has) you should first merge those changes back into your branch, so it stays in sync.

Newer versions of Subversion should make this process a little easier, but this works. Be sure to check out the Subversion manual on branching patterns.

Internet Explorer, Javascript and base elements

Internet Explorer treats the base element a bit diffently from other browser. I ran into the issue when trying to change the current page’s hash through javascript:

window.location.hash = 'some_value';

Internet explorer took the entire base URL and prepended it to the hash, resulting in an URL like http://domain.tld/http://domain.tld/#some_value. That’s clearly not my intention.

The trick lies in the href attribute for links. This actually points to the faulty long url, while its actual attribute value is only the hash:

<a id="link" href="#some_value">...</a>
// IE: http://domain.tld/#some_value
// other browser: #some_value
$('#link').attr('href'); 

The trick is to replace anything before the pound when reading the href value, like so:

$('#link').attr('href').replace(/^.*(?=#)/, '');

And when trying to find links pointing at #some_value to not be too restrictive with your selector:

// finds 1 in other browsers, nothing in IE
$('a[href="#some_value"]')

// works like expected in all browsers; Note the *
$('a[href*="#some_value"]')

Tricky stuff!

Removing deleted files from the Git index

When working with Git it can be cumbersome to have to remove files from the index (marking them deleted rahter missing) if you did not delete them using git-rm. Here’s bash one-liner for that:

git rm $(git ls-files -d)

I’ve got that aliased to grd (Git Remove Deleted).

Argument-specific memoization

There is another way of memoizing expensive operations in JavaScript, which is also fit for argument-specific results:

base._fooCache = {};
base.foo = function(arg) {
    if(base._fooCache[arg] === undefined) {
        base. _fooCache[arg] = ...expensive operation... 
    }
    return base. _fooCache[arg];
}; 

This just keeps a local key/value cache of the result of the expensive operation for the given argument. This only works for a single argument right now, but I guess it could be extended to multiple arguments.

Awesome JavaScript memoization

Here’s an easy way to memoize expensive Javascript functions. It introduces slightly obscure code and an extra function call, but if your operation is expensive enough to memoize, it is probably worth the extra overhead:

this.foo = function(){
    var foo = expensive_operation();
    return (this.foo = function() { return foo; })();
};

What this function does is redefine itself, so on subsequent calls it only returns a static value. Neat.

Using git stash branch

When using Git I sometimes end up with a bunch of changes that would really be better off in a feature branch. Here’s a quick way to take those changes in your working copy and start a feature branch quickly:

git stash
git stash branch my_branch_title

From the docs on git stash branch:

Creates and checks out a new branch named starting from the commit at which the was originally created, applies the changes recorded in to the new working tree and index, then drops the if that completes successfully. When no is given, applies the latest one.

Awesome.