Skip to content

Click Events From Hell

Click events are a godsend from hell. Here's how you can avoid at least one of the many problems they bring to the table: the end of a drag is not a click.

Back in the days I learned most of my current knowledge of event handling by reading @ppk's quirksmode. If you don't know how mouse events behave (across browsers) read up on the topic.

Mouse events have been driving me crazy the last couple of days. especially the fact that a click-event is triggered for every mousedown and mouseup on the same element. This is even true if the mouse has been moved as you can see in this simple draggable demo:

A click? really? Sadly yes, that is how those darn mouse events are defined. But fear not, we can throw some javascript at the problem. In mousedown and mouseup we can capture the pointer's position. In mouseup we compare the current pointer position to where it was when mousedown was triggered. If the positions are within a certain threshold, the mousedown/up combination's intention was indeed a click. But if the positions are too far off, we've most likely dragged something around and in turn do not expect the click event to be triggered. In our little event hell, there's no way to prevent a click event from being triggered, so we need to augment the click event handler a bit. Wrapping things up in a bit of delegated events, we get to the following snippet. (see the jsfiddle)

// mouse move click prevention
(function($){
    var $doc = $(document),
        moved = false,
        pos = {x: null, y: null},
        abs = Math.abs,
        mclick = {
            'mousedown.mclick': function(e) {
                pos.x = e.pageX;
                pos.y = e.pageY;
                moved = false;
            },
            'mouseup.mclick': function(e) {
                moved = abs(pos.x - e.pageX) > $.clickMouseMoved.threshold
                    || abs(pos.y - e.pageY) > $.clickMouseMoved.threshold;
            }
        };
   
    $doc.on(mclick);
    // globally valid test if the current click was indeed a click
    $.clickMouseMoved = function () {
        return moved;
    };
    // expose threshold configuration
    $.clickMouseMoved.threshold = 3;
})(jQuery);

Now we can add a test to our click handlers and ignore those pesky non-click clicks:

$(selector).on('click', function(e) {
    if ($.clickMouseMoved()) {
        // oh no, the mouse moved!
        return;
    }
   
    // do something clicky
});

Comments

Display comments as Linear | Threaded

No comments

The author does not allow comments to this entry