5

When the user presses enter I want the cursor to move to a new line, but if they are currently indented by two tabs, then the cursor should stay indented two tabs.

I have already implemented the ignore tab event to stop the focus moving within the page, so I'm now just looking for the logic to keep the tab level on new line.

if(e.keyCode === 13){

    //Logic here
}
JMac
  • 523
  • 1
  • 5
  • 17
  • 2
    You'll have to maintain a form class-local variable that holds the current tab level, and then append tabs or spaces to the end of the textarea text as required to maintain that tab level. You'll also need to hook shift-tab so that the user can reduce the tab level if he wants to. – Robert Harvey Feb 11 '14 at 23:27

3 Answers3

5

http://jsfiddle.net/DVKbn/

$("textarea").keydown(function(e){
    if(e.keyCode == 13){

        // assuming 'this' is textarea

        var cursorPos = this.selectionStart;
        var curentLine = this.value.substr(0, this.selectionStart).split("\n").pop();
        var indent = curentLine.match(/^\s*/)[0];
        var value = this.value;
        var textBefore = value.substring(0,  cursorPos );
        var textAfter  = value.substring( cursorPos, value.length );

        e.preventDefault(); // avoid creating a new line since we do it ourself
        this.value = textBefore + "\n" + indent + textAfter;
        setCaretPosition(this, cursorPos + indent.length + 1); // +1 is for the \n
    }
});

function setCaretPosition(ctrl, pos)
{

    if(ctrl.setSelectionRange)
    {
        ctrl.focus();
        ctrl.setSelectionRange(pos,pos);
    }
    else if (ctrl.createTextRange) {
        var range = ctrl.createTextRange();
        range.collapse(true);
        range.moveEnd('character', pos);
        range.moveStart('character', pos);
        range.select();
    }
}
Endless
  • 34,080
  • 13
  • 108
  • 131
1

I improved the answer by Endless by using execCommand 'insertText' instead of modifying textarea.value.

Advantages:

  • Maintains undo-redo history of the <textarea>.
  • Maintains native behavior where any selected text is deleted.
  • Does not lag when value is 4000+ characters.
  • Shorter, simpler code.

Disadvantages:

  • Currently not supported by Firefox. (Use solution by Endless as fallback.)

$('textarea').on('keydown', function(e) {
    if (e.which == 13) { // [ENTER] key
      event.preventDefault()  // We will add newline ourselves.

      var start = this.selectionStart;
      var currentLine = this.value.slice(0, start).split('\n').pop();
      var newlineIndent = '\n' + currentLine.match(/^\s*/)[0];

      if (!document.execCommand('insertText', false, newlineIndent)) {
          // Add fallback for Firefox browser:
          // Modify this.value and update cursor position as per solution by Endless.
      }
    }
  

});
<textarea style="width:99%;height:99px;">        I am indented by 8 spaces.
 I am indented by a tab.</textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Leftium
  • 16,497
  • 6
  • 64
  • 99
0

Must say solutions based on one key press are obscure because people also like pasting text. Use input event instead. You can make it in jQuery like so:

$('textarea').on('input', function(e) {
var el = $(this);
var cur = $(this).prop('selectionStart'); // retrieve current caret position before setting value
var text = $(this).val();
var newText = text.replace(/^(.+)\t+/mg, '$1'); // remove intermediate tabs
newText = newText.replace(/^([^\t]*)$/mg, '\t\t$1'); // add two tabs in the beginning of each line

if (newText != text) { // If text changed...
 $(this).val(newText); // finally set value
 // and reset caret position shifted right by one symbol
 $(this).prop('selectionStart', cur + 1);
 $(this).prop('selectionEnd', cur + 1);
}
});
<textarea></textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

By the way I'm too lazy to explain how to watch tab count needed for user, this one just inserts two tabs on every line.

vintprox
  • 931
  • 1
  • 11
  • 24