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?
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?
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:
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.
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.
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>
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>
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;
}
}
}
});
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;
}
}
}
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:
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, ' ');
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;
}
I've tried some solutions and none of them worked, so I came up with this:
document.addEventListener('keydown', (e) => {
if (e.code === 'Tab') {
e.preventDefault();
const TAB_WIDTH = 4;
//* Apply 1 space for every tab width
document.execCommand('insertText', false, ' '.repeat(TAB_WIDTH));
}
});
$("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
}
});