Intentionality & adverse effects


After my post on perceived speed the question was posed to me as to whether it was possible for a response to a user action to happen too quickly.

The classic example given here is a CSS and/or JavaScript powered dropdown menu which triggers immediately onhover or onmouseover causing problems for users as they move their mouse past the menu - triggering it to open unintentionally. The solution occasionally offered is to place a delay on the menu opening.

The user does not want a delay on the menu any more than they want their computer to take a long time to boot up or to have a slow internet connection. I’m happy to be proved wrong by some actual user testing on this but I don’t think it would make any difference to the point I’m trying to illustrate. The user has not asked for a delay. No matter how cute the transition, there is ultimately no pleasure in waiting for a menu to appear, time and time again.

The delay on the menu in such an instance is put in place to avoid having the UI respond to a user action in a way that the user did not intend. It is imperfect in that it does not actually try to decide whether the user wants the menu to open or not but instead says “if the user keeps the mouse of the menu for more than this amount of time then we assume that they intend for the menu to open.

In technical terms - a guess.

The problem with an unrefined guess is that it’s a shotgun approach. It might hit the target but there’s likely to be collateral damage or, as they call them in pharmaceuticals, adverse effects.

This happens too often on the web. Users have been slipped roofies by way of popup windows, broken URL’s, back button trickery, slow pages and a plethora of other embarrasments that were dreamt up by people thinking that they were only solving the problem at hand when in fact they were simultaneously creating other problems. All too often, JavaScript has been one of the tools used to do so.

The problem for JavaScript runs deeper too, with the programming environment (the DOM) inflicting pain on programmers at the other end. Here’s a cute one from IE that you may have seen:

document.body.onbeforeunload = function() {
    alert("about to unload");
}

The onbeforeunload event will actually fire in IE if a pseudo-protocol link href="javascript:" is clicked or one is pasted into the address bar. The problem with this, and with too many DOM quirks in various browser is that it goes against how the programmer rightly thinks what they do influences the universe. It goes against intentionality - just like the exampes inflicted on website users. Links lead to content - having the browser open a popup window is a side-effect, introducing effects on the user that they did not ask for. Any JavaScript developer can feel a real sense of empathy for their users on that point alone.

So, back to the dropdown menu, what’s a better alternative than a delay?

One option is to poll the mouse position to determine mouse velocity. Something like this:

(function() {
    var nav = document.getElementById("nav"),
        vector = [],
        lvector = [],
        velocity = 0,
        t,
        interval = 25,
        threshold = 0.5,
        fire = function() {
            
            
            // display the dropdown
            
            
        },
        load = function() {
            vector = [];
            lvector = [];
            
            
            // remove the dropdown


            return function() {
                fire();
                trigger = function() {};
            };
        },
        trigger = load(),
        sample = function() {
            velocity = vector[2]/interval;
            if (lvector.length == 3) {
                if (lvector[2] === vector[2] || threshold >= velocity) {
                    trigger();
                }
            }
            lvector = [].concat(vector);
        };
        
    //
    nav.onmouseover = function(e) {
        document.onmousemove = function(e) {
            vector[0] = (e || window.event).clientX - (lvector[0] || 0);
            vector[1] = (e || window.event).clientY - (lvector[1] || 0);
            vector[2] = Math.sqrt(Math.pow(vector[0], 2) + Math.pow(vector[1], 2));
        };
        t = window.setTimeout(function() {
            sample();
            t = window.setTimeout(arguments.callee, interval);
        }, interval);
    };
    //
    nav.onmouseout = function() {
        document.onmousemove = null;
        clearTimeout(t);
        // re-cock the trigger
        trigger = load();
    };
});

The example above helps to reduce a “delay when the user doesn’t want a delay” effect at the expense of introducing more work for the CPU. The reader may, or many not, find this an acceptable trade-off in individual circumstances.

Note that this code currently handles the mouse velocity only. It would need to be extended to correctly handle a dropdown menu completely. I may provide an update when I get time.

This example is provided in a slightly altered form as a download: nolib.velocitor.js.



CoffeeScript in Action


CoffeeScript in Action book cover

I'm the author. Get it from Manning.