Sunday, November 11, 2012

Snippet: Eating The Backspace Key on Data Entry Screens (Or, How I Became A Secret Hero)

Have you ever been filling out a form on the web and accidentally navigated away from the form…and lost your work? Ouch. There’re few things as frustrating as retyping something into a computer.

In addition to the typical things that a good web programmer does to help prevent data loss (e.g. use simple forms, prompt before leaving a dirty form, don’t break the forward/back buttons, background-save frequent drafts, etc.), here’s a technique I’ve used in a few projects that has silently saved people from some suffering:

Disable the keyboard shortcut for the back button via the backspace key

In case you didn’t know, the backspace key is typically mapped to the back button. Now imagine you’re updating a large text area with all kinds of thought provoking wisdom. Then you tab away from the text area or click off of it for some reason. Some time later you might hit the backspace key to delete the last letter you typed. Unfortunately, since your focus isn’t in a text area or input, you are navigated away from the page, losing your masterpiece.

It’s simple to eat that keyboard shortcut in Javascript. Here’s a snippet that uses jQuery since jQuery’s pretty much everywhere:

    $(document).keydown(function (e) {
        if (e.which == 8 /* backspace */
            && !$(e.target).is('input,textarea')) 
        { 
            e.preventDefault(); 
        }
    });

That is, if the keydown event fires and it’s backspace key and we’re not in an input or textarea, stop the keydown event. We can liven this up a little by interrupting the event if it’s readonly or disabled. This answer on Stackoverflow by erikkallen covers that case, too:

    $(document).unbind('keydown').bind('keydown', function (event) {
        var doPrevent = false;
        if (event.keyCode === 8) {
            var d = event.srcElement || event.target;
            if ((d.tagName.toUpperCase()==='INPUT' && 
                    (d.type.toUpperCase()==='TEXT' || d.type.toUpperCase()==='PASSWORD')) 
                || d.tagName.toUpperCase() === 'TEXTAREA') {

                doPrevent = d.readOnly || d.disabled;
            }
            else {
                doPrevent = true;
            }
        }

        if (doPrevent) {
            event.preventDefault();
        }
    });

And if you don’t want to use any jQuery, here’s the pure JS version (with more thanks to Stackoverflow):

    document.onkeydown = function (event) {
        var doPrevent = false;
        var keyCode = event.charCode ? event.charCode : event.keyCode;

        if(keyCode !== 8 /* backspace */){
            return;
        }
        
        var d = event.srcElement || event.target;
        if ((d.tagName.toUpperCase()==='INPUT' && 
                (d.type.toUpperCase()==='TEXT' || d.type.toUpperCase()==='PASSWORD')) 
            || d.tagName.toUpperCase() === 'TEXTAREA') {

            doPrevent = d.readOnly || d.disabled;
        }
        else {
            doPrevent = true;
        }

        if (doPrevent) {
            event.preventDefault();
        }
    };​

It’s the little things…