3

Given a textarea, is there any way to use jQuery to enable the ability for people to use the "tab" key that actually ..inserts a tab, instead of jumping focus to the next form element on the page?

I've followed Capturing TAB key in text box and while this does work, I'm looking to try and wrap this into a jQuery plugin to make a specific textbox tabbable. The problem is, I'm not entirely understanding how to apply the concept of these 'listeners' to objects that corrospond to jQuery.

Does anyone have some leads on where I could start?

Community
  • 1
  • 1
Ciel
  • 17,312
  • 21
  • 104
  • 199

5 Answers5

20

I've just written a jQuery plug-in to do this that works in all major browsers. It uses keydown and keypress to bind event listeners to a set of of elements. The event listeners prevent the default behaviour for the Tab key, while the keydown listener also manually inserts a tab character at the caret/selection.

Here it is in action: http://www.jsfiddle.net/8segz/

Here's an example use:

$(function() {
    $("textarea").allowTabChar();
});

Here's the plug-in code:

(function($) {
    function pasteIntoInput(el, text) {
        el.focus();
        if (typeof el.selectionStart == "number") {
            var val = el.value;
            var selStart = el.selectionStart;
            el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
            el.selectionEnd = el.selectionStart = selStart + text.length;
        } else if (typeof document.selection != "undefined") {
            var textRange = document.selection.createRange();
            textRange.text = text;
            textRange.collapse(false);
            textRange.select();
        }
    }

    function allowTabChar(el) {
        $(el).keydown(function(e) {
            if (e.which == 9) {
                pasteIntoInput(this, "\t");
                return false;
            }
        });

        // For Opera, which only allows suppression of keypress events, not keydown
        $(el).keypress(function(e) {
            if (e.which == 9) {
                return false;
            }
        });
    }

    $.fn.allowTabChar = function() {
        if (this.jquery) {
            this.each(function() {
                if (this.nodeType == 1) {
                    var nodeName = this.nodeName.toLowerCase();
                    if (nodeName == "textarea" || (nodeName == "input" && this.type == "text")) {
                        allowTabChar(this);
                    }
                }
            })
        }
        return this;
    }
})(jQuery);
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 2
    You are like Dr. Pepper on a bad day. This is awesome. – Ciel Dec 13 '10 at 19:00
  • Nice snippet. I know it's been 4 years but... how would you add that action to the undo history? The browser doesn't seem to allow undoing unintentional tabs using this code. Also, with `div[contenteditable]`, the element doesn't seem to have `selectionStart` or `document.selection` defined. Is there a workaround for "contenteditable"? Is there a more fleshed out pluggin of this that you know of? Thanks in advance. – Phil Tune Oct 29 '15 at 21:30
4

Try this simple jQuery function:

$.fn.getTab = function () {
    this.keydown(function (e) {
        if (e.keyCode === 9) {
            var val = this.value,
                start = this.selectionStart,
                end = this.selectionEnd;
            this.value = val.substring(0, start) + '\t' + val.substring(end);
            this.selectionStart = this.selectionEnd = start + 1;
            return false;
        }
        return true;
    });
    return this;
};

$("textarea").getTab();
// You can also use $("input").getTab();
3

Have your plugin do something like this:

$(inputElementSelector).keypress(function(event) {
    if (event.keyCode == 9) {
        //enter your tab behavior here
    }
}
mmurch
  • 498
  • 5
  • 14
2

Pass this function a jQuery selector that matches the elements you want to enable the Tab character in. For instance: BindTabKeyAsCharacter(".about_us textarea");

function BindTabKeyAsCharacter(selector) {
    $(document).ready(function () {
        $(selector).each(function () {
            $(this).keydown(function () {
                var e = (window.event) ? event : e;
                if (e.keyCode == 9) { // Tab
                    if (window.event) {
                        window.event.returnValue = false;
                    }
                    else {
                        if (e.cancelable) { e.preventDefault(); }
                    }
                    var before, after;
                    if (document.selection) {
                        this.selection = document.selection.createRange();
                        this.selection.text = String.fromCharCode(9);
                    } else {
                        before = this.value.substring(0, this.selectionStart);
                        after = this.value.substring(this.selectionEnd,     this.value.length);
                        this.value = before + String.fromCharCode(9) + after;
                    }
                    var newCursorPos = before.length + String.fromCharCode(9).length;
                    if (this.setSelectionRange) {
                        this.focus();
                        this.setSelectionRange(newCursorPos, newCursorPos);
                    } else if (this.createTextRange) {
                        var range = this.createTextRange();
                        range.collapse(true);
                        range.moveEnd('character', newCursorPos);
                        range.moveStart('character', newCursorPos);
                        range.select();
                    }
                }
            });
        });
    });
}
Gerbus
  • 2,554
  • 27
  • 22
0

To the code from Tim Down: I used it in my project and got a strange bug. When I insert a simple space in the beginning of a row the content will not update after submit. I got a lot of jQuery and Ajax Code in my project for textareas. But preventDefault() solved my bug:

...
function allowTabChar(el) {
    $(el).keydown(function(e) {
        if (e.which == 9) {
            pasteIntoInput(this, \"\t\");
            e.preventDefault();  // <- I had to add this
            $(el).change();      // To fire change event for my Ajax Code
            return false;
        }
    });
 ...