Read my book

I wrote books about Webpack and React. Check them out!

Thursday, January 3, 2013

jQuery Event Handlers Rethought

There are times when you need to write simple jQuery event handlers such as these:
$('.gallery_item').on('mouseenter mouseleave', function() {
    $('a', $(this)).toggle();
});
Even though it's not a lot of code I believe it could be less. As a result I came up with a little hack that allows you to write the same in the following syntax:
$('.gallery_item').on('mouseenter mouseleave', $e('a', 'this').toggle);
The magic lies within $e. It is a function that constructs aforementioned functions and has some knowledge of the way jQuery works. It introspects the jQuery API and connects the optional selectors with it.

I've included its full definition below. As I don't want to introspect the API every time $e is called I decided to memoize it. In other words it caches the initial results to the function itself. Performance is probably not an issue but I felt this was a good idea anyway.

It's a bit debatable whether "constructor.prototype" should be used. I cannot guarantee it works in each browser, it likely won't. It is possible to replace that bit with a simple array that contains the names of the jQuery methods you want to access. If someone knows a better way, let me know!
function $e(selector, scope) {
    if(selector) {
        return memoize($e, '_sel',
            function() {
                return selTemplate(function($elem, v) {
                    $(getThis(selector, $elem), getThis(scope, $elem))[v]();
                });
            }
        );
    }

    return memoize($e, '_nosel',
        function() {
            return selTemplate(function($elem, v) {
                $elem[v]();
            });
        }
    );

    function getThis(v, that) {
        return v === 'this'? that: v;
    }

    function selTemplate(cb) {
        var ret = {};
    
        $.each(Object.keys($('body').constructor.prototype),    
            function(i, v) {
                ret[v] = function() {
                    cb($(this), v);
                };
        });

        return ret;
    }
}

function memoize(fn, prop, cb) {
    var ret = fn[prop];
  
    if(ret) return ret;

    fn[prop] = cb();

    return fn[prop];
}

I've set up a little demo at JS Bin. You should be able to see it below:

Shortcut demo