How to handle <tab> in textarea?

JavascriptJqueryHtml

Javascript Problem Overview


I would like a textarea that handles a situation of pressing tab key.

In default case if you press a tab key then focus leaves the textarea. But what about the situation when user wants to type tab key in textarea?

Can I catch this event and return focus to the textarea and add a tab to a current cursor position?

Javascript Solutions


Solution 1 - Javascript

You can: http://jsfiddle.net/sdDVf/8/.


$("textarea").keydown(function(e) {
    if(e.keyCode === 9) { // tab was pressed
        // get caret position/selection
        var start = this.selectionStart;
        var end = this.selectionEnd;

        var $this = $(this);
        var value = $this.val();

        // set textarea value to: text before caret + tab + text after caret
        $this.val(value.substring(0, start)
                    + "\t"
                    + value.substring(end));

        // put caret at right position again (add one for the tab)
        this.selectionStart = this.selectionEnd = start + 1;

        // prevent the focus lose
        e.preventDefault();
    }
});

Solution 2 - Javascript

Here is a modified version of pimvdb's answer that doesn't need JQuery:

document.querySelector("textarea").addEventListener('keydown',function(e) {
	if(e.keyCode === 9) { // tab was pressed
		// get caret position/selection
		var start = this.selectionStart;
		var end = this.selectionEnd;

		var target = e.target;
		var value = target.value;

		// set textarea value to: text before caret + tab + text after caret
		target.value = value.substring(0, start)
					+ "\t"
					+ value.substring(end);

		// put caret at right position again (add one for the tab)
		this.selectionStart = this.selectionEnd = start + 1;

		// prevent the focus lose
		e.preventDefault();
	}
},false);

I tested it in Firefox 21.0 and Chrome 27. Don't know if it works anywhere else.

Solution 3 - Javascript

Good god, all previous answers failed to provide the commonly decent (i.e. for programmers) tab control.

That is, a hitting TAB on selection of lines will indent those lines, and SHIFTTAB will un-indent them.

_edited (Nov 2016): keyCode replaced with charCode || keyCode, per KeyboardEvent.charCode - Web APIs | MDN

(function($) {
  $.fn.enableSmartTab = function() {
    var $this;
    $this = $(this);
    $this.keydown(function(e) {
      var after, before, end, lastNewLine, changeLength, re, replace, selection, start, val;
      if ((e.charCode === 9 || e.keyCode === 9) && !e.altKey && !e.ctrlKey && !e.metaKey) {
        e.preventDefault();
        start = this.selectionStart;
        end = this.selectionEnd;
        val = $this.val();
        before = val.substring(0, start);
        after = val.substring(end);
        replace = true;
        if (start !== end) {
          selection = val.substring(start, end);
          if (~selection.indexOf('\n')) {
            replace = false;
            changeLength = 0;
            lastNewLine = before.lastIndexOf('\n');
            if (!~lastNewLine) {
              selection = before + selection;
              changeLength = before.length;
              before = '';
            } else {
              selection = before.substring(lastNewLine) + selection;
              changeLength = before.length - lastNewLine;
              before = before.substring(0, lastNewLine);
            }
            if (e.shiftKey) {
              re = /(\n|^)(\t|[ ]{1,8})/g;
              if (selection.match(re)) {
                start--;
                changeLength--;
              }
              selection = selection.replace(re, '$1');
            } else {
              selection = selection.replace(/(\n|^)/g, '$1\t');
              start++;
              changeLength++;
            }
            $this.val(before + selection + after);
            this.selectionStart = start;
            this.selectionEnd = start + selection.length - changeLength;
          }
        }
        if (replace && !e.shiftKey) {
          $this.val(before + '\t' + after);
          this.selectionStart = this.selectionEnd = start + 1;
        }
      }
    });
  };
})(jQuery);

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

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea rows="10" cols="80">
/* Just some code to edit with our new superTab */
(function($) {
    $.fn.enableSmartTab = function() {
        $this = $(this);
        $this.keydown(function(e) {
            if ((e.charCode === 9 || e.keyCode === 9) && !e.metaKey && !e.ctrlKey && !e.altKey) {
                e.preventDefault();
            }
        }
    }
}
</textarea>

Solution 4 - Javascript

In Vanilla (Default) JS this would be:

var textareas = document.getElementsByTagName('textarea');

if ( textareas ) { for ( var i = 0; i < textareas.length; i++ ) { textareas[i].addEventListener( 'keydown', function ( e ) { if ( e.which != 9 ) return;

  var start 			= this.selectionStart;
  var end				  = this.selectionEnd;

  this.value 			= this.value.substr( 0, start ) + "\t" + this.value.substr( end );
  this.selectionStart = this.selectionEnd = start + 1;

  e.preventDefault();
  return false;
});

} }

textarea { border: 1px solid #cfcfcf; width: 100%; margin-left: 0px; top: 0px; bottom: 0px; position: absolute; }

Solution 5 - Javascript

Enable tabbing inside (multiple) textarea elements

Correcting @alexwells answer and enable a live demo

var textAreaArray = document.querySelectorAll("textarea");
    for (var i = textAreaArray.length-1; i >=0;i--){
        textAreaArray[i].addEventListener('keydown',function(e) {
            if(e.keyCode === 9) { // tab was pressed
                // get caret position/selection
                var start = this.selectionStart;
                var end = this.selectionEnd;

                var target = e.target;
                var value = target.value;

                // set textarea value to: text before caret + tab + text after caret
                target.value = value.substring(0, start)
                            + "\t"
                            + value.substring(end);

                // put caret at right position again (add one for the tab)
                this.selectionStart = this.selectionEnd = start + 1;

                // prevent the focus lose
                e.preventDefault();
            }
        },false);
    }

<textarea rows="10" cols="80"></textarea>
   <textarea rows="10" cols="80"></textarea>

Solution 6 - Javascript

Found this while searching google. I made a really short one that can also indent and reverse indent selections of text:

	jQ(document).on('keydown', 'textarea', function(e) {
	    if (e.keyCode !== 9) return;
	    var Z;
      	var S = this.selectionStart;
	    var E = Z = this.selectionEnd;
	    var A = this.value.slice(S, E);
	    A = A.split('\n');
	    if (!e.shiftKey)
	        for (var x in A) {
	            A[x] = '\t' + A[x];
	            Z++;
	        }
	    else
	        for (var x in A) {
	            if (A[x][0] == '\t')
	                A[x] = A[x].substr(1);
	            Z--;
	        }
	    A = A.join('\n');
	    this.value = this.value.slice(0, S) + A + this.value.slice(E);
	    this.selectionStart = S != E ? S : Z;;
	    this.selectionEnd = Z;
	    e.preventDefault();
	});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsergzachView Question on Stackoverflow
Solution 1 - JavascriptpimvdbView Answer on Stackoverflow
Solution 2 - JavascriptalexwellsView Answer on Stackoverflow
Solution 3 - JavascriptOrwellophileView Answer on Stackoverflow
Solution 4 - JavascriptMarkView Answer on Stackoverflow
Solution 5 - JavascriptAmjadView Answer on Stackoverflow
Solution 6 - JavascriptJohn SmithView Answer on Stackoverflow