Use tab to indent in textarea

JavascriptHtmlTextarea

Javascript Problem Overview


I have a simple HTML textarea on my site.

Right now, if you click Tab in it, it goes to the next field. I would like to make the tab button indent a few spaces instead.

How can I do this?

Javascript Solutions


Solution 1 - Javascript

Borrowing heavily from other answers for similar questions (posted below)...

document.getElementById('textbox').addEventListener('keydown', function(e) {
  if (e.key == 'Tab') {
    e.preventDefault();
    var start = this.selectionStart;
    var end = this.selectionEnd;

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

    // put caret at right position again
    this.selectionStart =
      this.selectionEnd = start + 1;
  }
});

<input type="text" name="test1" />
<textarea id="textbox" name="test2"></textarea>
<input type="text" name="test3" />

https://stackoverflow.com/questions/1314450/jquery-how-to-capture-the-tab-keypress-within-a-textbox

https://stackoverflow.com/questions/6140632/how-to-handle-tab-in-textarea

Solution 2 - Javascript

var textareas = document.getElementsByTagName('textarea');
var count = textareas.length;
for(var i=0;i<count;i++){
	textareas[i].onkeydown = function(e){
		if(e.keyCode==9 || e.which==9){
			e.preventDefault();
			var s = this.selectionStart;
			this.value = this.value.substring(0,this.selectionStart) + "\t" + this.value.substring(this.selectionEnd);
			this.selectionEnd = s+1; 
		}
	}
}

This solution does not require jQuery and will enable tab functionality on all textareas on a page.

Solution 3 - Javascript

As others have written, you can use JavaScript to capture the event, prevent the default action (so that the cursor does not shift focus) and insert a tab character.

But, disabling the default behavior makes it impossible to move the focus out of the text area without using a mouse. Blind users interact with web pages using the keyboard and nothing else -- they can't see the mouse pointer to do anything useful with it, so it's keyboard or nothing. The tab key is the primary way to navigate the document, and especially forms. Overriding the default behavior of the tab key will make it impossible for blind users to move the focus to the next form element.

So, if you're writing a web site for a broad audience, I'd recommend against doing this without a compelling reason, and provide some kind of alternative for blind users that doesn't trap them in the textarea.

Solution 4 - Javascript

For what it's worth, here's my oneliner, for what you all have been talking about in this thread:

<textarea onkeydown="if(event.keyCode===9){var v=this.value,s=this.selectionStart,e=this.selectionEnd;this.value=v.substring(0, s)+'\t'+v.substring(e);this.selectionStart=this.selectionEnd=s+1;return false;}">
</textarea>

Testest in latest editions of Chrome, Firefox, Internet Explorer and Edge.

Solution 5 - Javascript

Here's my version of this, supports:

  • tab + shift tab
  • maintains undo stack for simple tab character inserts
  • supports block line indent/unindent but trashes undo stack
  • properly selects whole lines when block indent/unindent
  • supports auto indent on pressing enter (maintains undo stack)
  • use Escape key cancel support on next tab/enter key (so you can press Escape then tab out)
  • Works on Chrome + Edge, untested others.

$(function() { 
	var enabled = true;
	$("textarea.tabSupport").keydown(function(e) {

		// Escape key toggles tab on/off
		if (e.keyCode==27)
		{
			enabled = !enabled;
			return false;
		}

		// Enter Key?
		if (e.keyCode === 13 && enabled)
		{
			// selection?
			if (this.selectionStart == this.selectionEnd)
			{
				// find start of the current line
				var sel = this.selectionStart;
				var text = $(this).val();
				while (sel > 0 && text[sel-1] != '\n')
				sel--;

				var lineStart = sel;
				while (text[sel] == ' ' || text[sel]=='\t')
				sel++;

				if (sel > lineStart)
				{
					// Insert carriage return and indented text
					document.execCommand('insertText', false, "\n" + text.substr(lineStart, sel-lineStart));

					// Scroll caret visible
					this.blur();
					this.focus();
					return false;
				}
			}
		}

		// Tab key?
		if(e.keyCode === 9 && enabled) 
		{
			// selection?
			if (this.selectionStart == this.selectionEnd)
			{
				// These single character operations are undoable
				if (!e.shiftKey)
				{
					document.execCommand('insertText', false, "\t");
				}
				else
				{
					var text = this.value;
					if (this.selectionStart > 0 && text[this.selectionStart-1]=='\t')
					{
						document.execCommand('delete');
					}
				}
			}
			else
			{
				// Block indent/unindent trashes undo stack.
				// Select whole lines
				var selStart = this.selectionStart;
				var selEnd = this.selectionEnd;
				var text = $(this).val();
				while (selStart > 0 && text[selStart-1] != '\n')
					selStart--;
				while (selEnd > 0 && text[selEnd-1]!='\n' && selEnd < text.length)
					selEnd++;

				// Get selected text
				var lines = text.substr(selStart, selEnd - selStart).split('\n');

				// Insert tabs
				for (var i=0; i<lines.length; i++)
				{
					// Don't indent last line if cursor at start of line
					if (i==lines.length-1 && lines[i].length==0)
						continue;

					// Tab or Shift+Tab?
					if (e.shiftKey)
					{
						if (lines[i].startsWith('\t'))
							lines[i] = lines[i].substr(1);
						else if (lines[i].startsWith("    "))
							lines[i] = lines[i].substr(4);
					}
					else
						lines[i] = "\t" + lines[i];
				}
				lines = lines.join('\n');

				// Update the text area
				this.value = text.substr(0, selStart) + lines + text.substr(selEnd);
				this.selectionStart = selStart;
				this.selectionEnd = selStart + lines.length; 
			}

			return false;
		}

		enabled = true;
		return true;
	});
});

textarea
{
  width: 100%;
  height: 100px;
  tab-size: 4;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="tabSupport">if (something)
{
	// This textarea has "tabSupport" CSS style
	// Try using tab key
	// Try selecting multiple lines and using tab and shift+tab
	// Try pressing enter at end of this line for auto indent
	// Use Escape key to toggle tab support on/off
	//     eg: press Escape then Tab to go to next field
}
</textarea>
<textarea>This text area doesn't have tabSupport class so disabled here</textarea>

Solution 6 - Javascript

Modern way that both is straight-forward and does not lose the ability to undo (Ctrl+Z) the last changes.

$('#your-textarea').keydown(function (e) {
	var keyCode = e.keyCode || e.which;
	
	if (keyCode === $.ui.keyCode.TAB) {
		e.preventDefault();
		
		const TAB_SIZE = 4;
		
		// The one-liner that does the magic
		document.execCommand('insertText', false, ' '.repeat(TAB_SIZE));
	}
});

More about execCommand:


Edit:

As pointed out in the comment (and while this was once a "modern" solution), the feature has gone obsolete. Quoting the docs:

> This feature is obsolete. Although it may still work in some browsers, its use is discouraged since it could be removed at any time. Try to avoid using it.

Solution 7 - Javascript

I was getting nowhere fast trying to use @kasdega's answer in an AngularJS environment, nothing I tried seemed able to make Angular act on the change. So in case it's of any use to passers by, here's a rewrite of @kasdega's code, AngularJS style, which worked for me:

app.directive('ngAllowTab', function () {
    return function (scope, element, attrs) {
        element.bind('keydown', function (event) {
            if (event.which == 9) {
                event.preventDefault();
                var start = this.selectionStart;
                var end = this.selectionEnd;
                element.val(element.val().substring(0, start) 
                    + '\t' + element.val().substring(end));
                this.selectionStart = this.selectionEnd = start + 1;
                element.triggerHandler('change');
            }
        });
    };
});

and:

<textarea ng-model="mytext" ng-allow-tab></textarea>

Solution 8 - Javascript

This solution allows tabbing in an entire selection like your typical code editor, and untabbing that selection too. However, I haven't figured out how to implement shift-tab when there's no selection.

$('#txtInput').on('keydown', function(ev) {
    var keyCode = ev.keyCode || ev.which;

    if (keyCode == 9) {
        ev.preventDefault();
        var start = this.selectionStart;
        var end = this.selectionEnd;
        var val = this.value;
        var selected = val.substring(start, end);
        var re, count;

        if(ev.shiftKey) {
            re = /^\t/gm;
            count = -selected.match(re).length;
            this.value = val.substring(0, start) + selected.replace(re, '') + val.substring(end);
            // todo: add support for shift-tabbing without a selection
        } else {
            re = /^/gm;
            count = selected.match(re).length;
            this.value = val.substring(0, start) + selected.replace(re, '\t') + val.substring(end);
        }

        if(start === end) {
            this.selectionStart = end + count;
        } else {
            this.selectionStart = start;
        }

        this.selectionEnd = end + count;
    }
});

#txtInput {
  font-family: monospace;
  width: 100%;
  box-sizing: border-box;
  height: 200px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<textarea id="txtInput">
$(document).ready(function(){
	$("#msgid").html("This is Hello World by JQuery");
});
</textarea>

Solution 9 - Javascript

You have to write JS code to catch TAB key press and insert a bunch of spaces. Something similar to what JSFiddle does.

Check jquery fiddle:

HTML:

<textarea id="mybox">this is a test</textarea>

JavaScript:

$('#mybox').live('keydown', function(e) { 
  var keyCode = e.keyCode || e.which; 

  if (keyCode == 9) { 
    e.preventDefault(); 
    alert('tab pressed');
  } 
});
ā€‹

Solution 10 - Javascript

Multiple-line indetation script based on @kasdega solution.

$('textarea').on('keydown', function (e) {
	var keyCode = e.keyCode || e.which;

	if (keyCode === 9) {
		e.preventDefault();
		var start = this.selectionStart;
		var end = this.selectionEnd;
		var val = this.value;
		var selected = val.substring(start, end);
		var re = /^/gm;
		var count = selected.match(re).length;


		this.value = val.substring(0, start) + selected.replace(re, '\t') + val.substring(end);
		this.selectionStart = start;
		this.selectionEnd = end + count;
	}
});

Solution 11 - Javascript

Hold ALT and press 0,9 from numeric keypad. It works in google-chrome

Solution 12 - Javascript

Based on all that people had to say here on the answers, its just a combination of keydown(not keyup) + preventDefault() + insert a tab character at the caret. Something like:

var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
   e.preventDefault();
   insertAtCaret('txt', '\t')
}

The earlier answer had a working jsfiddle but it used an alert() on keydown. If you remove this alert, then it didnt work. I ve just added a function to insert a tab at the current cursor position in the textarea.

Here s a working jsfiddle for the same: http://jsfiddle.net/nsHGZ/

Solution 13 - Javascript

I see this subject is not solved. I coded this and it's working very well. It insert a tabulation at the cursor index. Without using jquery

<textarea id="myArea"></textarea>
<script>
document.getElementById("myArea").addEventListener("keydown",function(event){
    if(event.code==="Tab"){
        var cIndex=this.selectionStart;
        this.value=[this.value.slice(0,cIndex),//Slice at cursor index
            "\t",                              //Add Tab
            this.value.slice(cIndex)].join('');//Join with the end
        event.stopPropagation();
        event.preventDefault();                //Don't quit the area
        this.selectionStart=cIndex+1;
        this.selectionEnd=cIndex+1;            //Keep the cursor in the right index
    }
});
</script>

Solution 14 - Javascript

The simplest way I found to do that in modern browsers with vanilla JavaScript is:

  <textarea name="codebox"></textarea>
  
  <script>
  const codebox = document.querySelector("[name=codebox]")

  codebox.addEventListener("keydown", (e) => {
    let { keyCode } = e;
    let { value, selectionStart, selectionEnd } = codebox;

    if (keyCode === 9) {  // TAB = 9
      e.preventDefault();

      codebox.value = value.slice(0, selectionStart) + "\t" + value.slice(selectionEnd);

      codebox.setSelectionRange(selectionStart+2, selectionStart+2)
    }
  });
  </script>

Note that I used many ES6 features in this snippet for the sake of simplicity, you'll probably want to transpile it (with Babel or TypeScript) before deploying it.

Solution 15 - Javascript

The above answers all wipe undo history. For anyone looking for a solution that doesn't do that, I spent the last hour coding up the following for Chrome:

jQuery.fn.enableTabs = function(TAB_TEXT){
	// options
	if(!TAB_TEXT)TAB_TEXT = '\t';
	// text input event for character insertion
	function insertText(el, text){
		var te = document.createEvent('TextEvent');
		te.initTextEvent('textInput', true, true, null, text, 9, "en-US");
		el.dispatchEvent(te);
	}
	// catch tab and filter selection
	jQuery(this).keydown(function(e){
		if((e.which || e.keyCode)!=9)return true;
		e.preventDefault();
		var	contents = this.value,
			sel_start = this.selectionStart,
			sel_end = this.selectionEnd,
			sel_contents_before = contents.substring(0, sel_start),
			first_line_start_search = sel_contents_before.lastIndexOf('\n'),
			first_line_start = first_line_start_search==-1 ? 0 : first_line_start_search+1,
			tab_sel_contents = contents.substring(first_line_start, sel_end),
			tab_sel_contents_find = (e.shiftKey?new RegExp('\n'+TAB_TEXT, 'g'):new RegExp('\n', 'g')),
			tab_sel_contents_replace = (e.shiftKey?'\n':'\n'+TAB_TEXT);
			tab_sel_contents_replaced = (('\n'+tab_sel_contents)
				.replace(tab_sel_contents_find, tab_sel_contents_replace))
				.substring(1),
			sel_end_new = first_line_start+tab_sel_contents_replaced.length;
		this.setSelectionRange(first_line_start, sel_end);
		insertText(this, tab_sel_contents_replaced);
		this.setSelectionRange(first_line_start, sel_end_new);
	});
};

In short, tabs are inserted at the beginning of the selected lines.

JSFiddle: http://jsfiddle.net/iausallc/5Lnabspr/11/

Gist: https://gist.github.com/iautomation/e53647be326cb7d7112d

Example usage: $('textarea').enableTabs('\t')

Cons: Only works on Chrome as is.

Solution 16 - Javascript

I made one that you can access with any textarea element you like:

function textControl (element, event)
{
    if(event.keyCode==9 || event.which==9)
    {
        event.preventDefault();
        var s = element.selectionStart;
        element.value = element.value.substring(0,element.selectionStart) + "\t" + element.value.substring(element.selectionEnd);
        element.selectionEnd = s+1; 
    }
}

And the element would look like this:

<textarea onkeydown="textControl(this,event)"></textarea>

Solution 17 - Javascript

There is a library on Github for tab support in your textareas by wjbryant: Tab Override

This is how it works:

// get all the textarea elements on the page
var textareas = document.getElementsByTagName('textarea');

// enable Tab Override for all textareas
tabOverride.set(textareas);

Solution 18 - Javascript

Every input an textarea element has a onkeydown event. In the event handler you can prevent the default reaction of the tab key by using event.preventDefault() whenever event.keyCode is 9.

Then put a tab sign in the right position:

function allowTab(input)
{
    input.addEventListener("keydown", function(event)
    {
        if(event.keyCode == 9)
        {
            event.preventDefault();
            
            var input = event.target;
            
            var str = input.value;
            var _selectionStart = input.selectionStart;
            var _selectionEnd = input.selectionEnd;
            
            str = str.substring(0, _selectionStart) + "\t" + str.substring(_selectionEnd, str.length);
            _selectionStart++;
            
            input.value = str;
            input.selectionStart = _selectionStart;
            input.selectionEnd = _selectionStart;
        }
    });
}

window.addEventListener("load", function(event)
{
    allowTab(document.querySelector("textarea"));
});

html

<textarea></textarea>

Solution 19 - Javascript

Simple standalone script:

textarea_enable_tab_indent = function(textarea) {    
    textarea.onkeydown = function(e) {
        if (e.keyCode == 9 || e.which == 9){
            e.preventDefault();
            var oldStart = this.selectionStart;
            var before   = this.value.substring(0, this.selectionStart);
            var selected = this.value.substring(this.selectionStart, this.selectionEnd);
            var after    = this.value.substring(this.selectionEnd);
            this.value = before + "    " + selected + after;
            this.selectionEnd = oldStart + 4;
        }
    }
}

Solution 20 - Javascript

As an option to kasdega's code above, instead of appending the tab to the current value, you can instead insert characters at the current cursor point. This has the benefit of:

  • allows you to insert 4 spaces as an alternative to tab
  • undo and redo will work with the inserted characters (it won't with the OP)

so replace

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

with

    // set textarea value to: text before caret + tab + text after caret
    document.execCommand("insertText", false, '    ');

Solution 21 - Javascript

You can use the setRangeText() method available on the textarea element to do this natively.

HTML

<textarea id='my-textarea' onkeydown="handleKeyDown(event)"></textarea>

JS

const handleKeyDown = e => {
if (e.key === 'Tab') {
  	e.preventDefault();
    const textArea = e.currentTarget; // or use document.querySelector('#my-textarea');
    textArea.setRangeText(
      '\t',
      textArea.selectionStart,
      textArea.selectionEnd,
      'end'
    );
  }
};

setRangeText is used for replacing text, but since we only want to insert a \t, we simply set the selection to the start and end of the current selection. The 'end' value tells the method to move the cursor to the end of the inserted text.

Bonus CSS

If you want to change the tab size, you can use the tab-size property on block elements. The default for most browsers is 8.

textarea {
  tab-size: 4;
}

Mozilla: HTMLInputElement.setRangeText()

Mozzila: Tab-Size

Solution 22 - Javascript

Here's a simple pure-JS approach that supports basic indenting and dedenting.

Unfortunately, it doesn't preserve the undo history or support block-level tabbing.

document.querySelectorAll('textarea').forEach(function(textarea)
{
    textarea.onkeydown = function(e)
    {
		if (e.keyCode === 9 || e.which === 9)
		{
			e.preventDefault();
			if (e.shiftKey && this.selectionStart)
			{
				if (this.value[this.selectionStart -1] === "\t")
				{
					var s = this.selectionStart;
					this.value = this.value.substring(0,this.selectionStart - 1) + this.value.substring(this.selectionEnd);
					this.selectionEnd = s-1; 
				}
          
			}
			
			if (!e.shiftKey)
			{
				var s = this.selectionStart;
				this.value = this.value.substring(0,this.selectionStart) + "\t" + this.value.substring(this.selectionEnd);
				this.selectionEnd = s+1; 
			}
		}
    }
});

Solution 23 - Javascript

if (e.which == 9) {
	e.preventDefault();
	var start = $(this).get(0).selectionStart;
	var end = $(this).get(0).selectionEnd;
	
	if (start === end) {
		$(this).val($(this).val().substring(0, start)
					+ "\t"
					+ $(this).val().substring(end));
		$(this).get(0).selectionStart =
		$(this).get(0).selectionEnd = start + 1;
	} else {
		var sel = $(this).val().substring(start, end),
			find = /\n/g,
			count = sel.match(find) ? sel.match(find).length : 0;
		$(this).val($(this).val().substring(0, start)
					+ "\t"
					+ sel.replace(find, "\n\t")
					+ $(this).val().substring(end, $(this).val().length));
		$(this).get(0).selectionStart =
		$(this).get(0).selectionEnd = end+count+1;
	}
}

Solution 24 - Javascript

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();

Solution 25 - Javascript

I had to make a function to do the same, It is simple to use, just copy this code to your script and use: enableTab( HTMLElement ) HTMLelement being something like document.getElementById( id )


The code is:

function enableTab(t){t.onkeydown=function(t){if(9===t.keyCode){var e=this.value,n=this.selectionStart,i=this.selectionEnd;return this.value=e.substring(0,n)+"	"+e.substring(i),this.selectionStart=this.selectionEnd=n+1,!1}}}

Solution 26 - Javascript

$("textarea").keydown(function(event) {
    if(event.which===9){
        var cIndex=this.selectionStart;
        this.value=[this.value.slice(0,cIndex),//Slice at cursor index
            "\t",                              //Add Tab
            this.value.slice(cIndex)].join('');//Join with the end
        event.stopPropagation();
        event.preventDefault();                //Don't quit the area
        this.selectionStart=cIndex+1;
        this.selectionEnd=cIndex+1;            //Keep the cursor in the right index
    }
});

Solution 27 - Javascript

If you really need tabs copy a tab from word or notepad and paste it in the text box where you want it

1 2 3

12 22 33

Unfortunately I think they remove the tabs from these comments though :) It will show as %09 in your POST or GET

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
Questionuser780483View Question on Stackoverflow
Solution 1 - JavascriptkasdegaView Answer on Stackoverflow
Solution 2 - Javascriptuser1949974View Answer on Stackoverflow
Solution 3 - JavascriptWill MartinView Answer on Stackoverflow
Solution 4 - JavascriptelgholmView Answer on Stackoverflow
Solution 5 - JavascriptBrad RobinsonView Answer on Stackoverflow
Solution 6 - JavascriptYom T.View Answer on Stackoverflow
Solution 7 - JavascriptstovrozView Answer on Stackoverflow
Solution 8 - JavascriptmpenView Answer on Stackoverflow
Solution 9 - JavascriptAziz ShaikhView Answer on Stackoverflow
Solution 10 - JavascriptMartinView Answer on Stackoverflow
Solution 11 - JavascriptkrishnaView Answer on Stackoverflow
Solution 12 - JavascriptwalmikView Answer on Stackoverflow
Solution 13 - JavascriptBibimissionView Answer on Stackoverflow
Solution 14 - JavascriptTelmo TrooperView Answer on Stackoverflow
Solution 15 - JavascriptiautomationView Answer on Stackoverflow
Solution 16 - JavascriptHellena BaileyView Answer on Stackoverflow
Solution 17 - JavascriptPho3nixHunView Answer on Stackoverflow
Solution 18 - JavascriptMartin WantkeView Answer on Stackoverflow
Solution 19 - JavascriptkungfoomanView Answer on Stackoverflow
Solution 20 - JavascriptdygView Answer on Stackoverflow
Solution 21 - JavascriptJohn GaltView Answer on Stackoverflow
Solution 22 - JavascriptPikamander2View Answer on Stackoverflow
Solution 23 - JavascriptrazorxanView Answer on Stackoverflow
Solution 24 - Javascriptuser3087089View Answer on Stackoverflow
Solution 25 - JavascriptDendromaniacView Answer on Stackoverflow
Solution 26 - JavascriptKonstantin XFlash StratigenasView Answer on Stackoverflow
Solution 27 - Javascriptuser4686585View Answer on Stackoverflow