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.

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!

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.