如何为 HTML 文本区域添加自动缩进?

发布于 2024-11-02 16:58:33 字数 1636 浏览 3 评论 0原文

我有 HTML 文本区域。我想修改它,使其实现自动缩进,即插入NEWLINE后,我想在新行的开头自动插入空格(空格的数量取决于上一行的缩进)。我发现,我可以通过注册一个监听“按键”事件的处理程序来做到这一点。现在我有一个选择:(a) 保留默认处理程序并在浏览器向 textarea.value 添加换行符后插入空格,或者 (b) 使用 PreventDefault() 并自行插入整个内容(即换行符和空格)。

在情况 (a) 中,如下面的代码所示,我的处理程序在浏览器添加换行符之前执行,因此空格(或用于说明的“--”)最终出现在该行的末尾,而不是在该行的开头新的。

在情况 (b) 中,如下面代码中的注释所示,文本被正确修改,但如果它导致光标移出文本区域视图,则内容不会滚动(很可能是因为内容滚动是默认处理的一部分) ),因此光标消失在文本区域边界后面,并且仅当我发送另一个击键(即不是换行符)时才会重新出现。

如何实现自动缩进的效果而又不丢失默认的滚动效果?

我知道这种效果可能可以通过延迟插入空格(例如使用 setTimeout())来近似,以便运行时有足够的时间来完成默认处理(即插入换行符和垂直滚动),但看起来像对我来说,这是一个巨大的混乱,并且引入了竞争条件,我担心它会在最意想不到的情况下(大量复制粘贴、由于其他操作而导致运行速度缓慢、高键盘重复率等)对我造成打击。理想情况下,我希望 (i) 在默认处理之后调用我的代码,或者 (ii) 能够阻止默认处理、运行我的代码并显式调用默认处理。如何实现?

谢谢!

Greg

PS:我对集成复杂的文本区域替换不感兴趣,例如Editarea(我使用一个,它在浏览器中非常脆弱)。

在 FF3 上测试。

<html>
  <head>
    <script type="text/javascript">
      function onKeyPressHandler(e) {
      if (e.which == 13) // ASCII newline
          {
              var start = this.selectionStart;
              var end = this.selectionEnd;
              var v = this.value;
              this.value = v.slice(0, start) + '--' + v.slice(end); // (a)

              // (b): this.value = v.slice(0, start) + '\n--' + v.slice(end);
              // (b): e.preventDefault();
      }
      }

      onload = function() {
      var editor = document.getElementById("editor");
      editor.addEventListener('keypress', onKeyPressHandler, false);
      } 
    </script>
  </head>
  <body>
    <textarea rows="20" cols="80" id="editor"></textarea>
  </body>
</html>

I have HTML textarea. I want to modify it, so that it implements auto-indentation, i.e. after NEWLINE is inserted, I want to automatically insert spaces at the beginning of the new line (the number of spaces depending on the indentation of the previous line). I figured out, that I can do it by registering a handler listening to 'keypress' event. Now I have a choice: (a) leave the default handler and insert the spaces AFTER the browser adds newline to the textarea.value, or (b) use preventDefault() and insert the whole thing (i.e. newline and spaces) by myself.

In case (a), illustrated by the code below, my handler is executed BEFORE the browser adds a newline, so the spaces (or '--' for illustration) end up at the end of the line, not at the beginning of the new one.

In case (b), shown in comments in the code below, the text is modified correctly, but if it results in cursor going out of the textarea view, the content is not scrolled (most likely because content scrolling is a part of default handling), so the cursor disappears behind the textarea boundary, and reappears only if I send another keystroke (i.e. not a newline).

How to achieve the auto-indentation effect without losing the default scrolling?

I know that this effect can possibly be approximated by delaying the insertion of spaces (e.g. with setTimeout()), so that the runtime has enough time to complete the default handling (i.e. insertion of the newline and vertical scrolling), but it seems like a huge kludge to me and introduction of a race condition that I am afraid is going to hit me under least expected circumstances (massive copy-paste, runtime slow due to other actions, high keyboard repetition rate etc.). Ideally I would like either (i) to have my code called after the default handling or (ii) to be able to prevent default handling, run my code, and explicitly call default handling. How to achieve it?

Thanks!

Greg

PS: I am not interested in integrating sophisticated textarea replacements, e.g. Editarea (I use one and it is very fragile across browsers).

Tested on FF3.

<html>
  <head>
    <script type="text/javascript">
      function onKeyPressHandler(e) {
      if (e.which == 13) // ASCII newline
          {
              var start = this.selectionStart;
              var end = this.selectionEnd;
              var v = this.value;
              this.value = v.slice(0, start) + '--' + v.slice(end); // (a)

              // (b): this.value = v.slice(0, start) + '\n--' + v.slice(end);
              // (b): e.preventDefault();
      }
      }

      onload = function() {
      var editor = document.getElementById("editor");
      editor.addEventListener('keypress', onKeyPressHandler, false);
      } 
    </script>
  </head>
  <body>
    <textarea rows="20" cols="80" id="editor"></textarea>
  </body>
</html>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

℡Ms空城旧梦 2024-11-09 16:58:33

我修改了 Leo 的答案来解决延迟问题(通过使用 keypress 而不是带有 setTimeout 的 keyup),以及导致编辑文本中间不起作用的错误。

$("textarea").keydown(function(e)
{
    if (e.which == 9) //ASCII tab
    {
        e.preventDefault();
        var start = this.selectionStart;
        var end = this.selectionEnd;
        var v = $(this).val();
        if (start == end)
        {
            $(this).val(v.slice(0, start) + "    " + v.slice(start));
            this.selectionStart = start+4;
            this.selectionEnd = start+4;
            return;
        }

        var selectedLines = [];
        var inSelection = false;
        var lineNumber = 0;
        for (var i = 0; i < v.length; i++)
        {
            if (i == start)
            {
                inSelection = true;
                selectedLines.push(lineNumber);
            }
            if (i >= end)
                inSelection = false;

            if (v[i] == "\n")
            {
                lineNumber++;
                if (inSelection)
                    selectedLines.push(lineNumber);
            }
        }
        var lines = v.split("\n");
        for (var i = 0; i < selectedLines.length; i++)
        {
            lines[selectedLines[i]] = "    " + lines[selectedLines[i]];
        }

        $(this).val(lines.join("\n"));
    }
});
$("textarea").keypress(function(e)
{
    if (e.which == 13) // ASCII newline
    {
        setTimeout(function(that)
        {
            var start = that.selectionStart;
            var v = $(that).val();
            var thisLine = "";
            var indentation = 0;
            for (var i = start-2; i >= 0 && v[i] != "\n"; i--)
            {
                thisLine = v[i] + thisLine;
            }
            for (var i = 0; i < thisLine.length && thisLine[i] == " "; i++)
            {

                indentation++;
             }
             $(that).val(v.slice(0, start) + " ".repeat(indentation) + v.slice(start));
             that.selectionStart = start+indentation;
             that.selectionEnd = start+indentation;  
}, 0.01, this);
     }
});
<textarea rows="20" cols="40"></textarea>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

I've modified Leo's answer to fix the delay problem (by using keypress instead of keyup with a setTimeout), and the bug that caused editing the middle of the text to not work.

$("textarea").keydown(function(e)
{
    if (e.which == 9) //ASCII tab
    {
        e.preventDefault();
        var start = this.selectionStart;
        var end = this.selectionEnd;
        var v = $(this).val();
        if (start == end)
        {
            $(this).val(v.slice(0, start) + "    " + v.slice(start));
            this.selectionStart = start+4;
            this.selectionEnd = start+4;
            return;
        }

        var selectedLines = [];
        var inSelection = false;
        var lineNumber = 0;
        for (var i = 0; i < v.length; i++)
        {
            if (i == start)
            {
                inSelection = true;
                selectedLines.push(lineNumber);
            }
            if (i >= end)
                inSelection = false;

            if (v[i] == "\n")
            {
                lineNumber++;
                if (inSelection)
                    selectedLines.push(lineNumber);
            }
        }
        var lines = v.split("\n");
        for (var i = 0; i < selectedLines.length; i++)
        {
            lines[selectedLines[i]] = "    " + lines[selectedLines[i]];
        }

        $(this).val(lines.join("\n"));
    }
});
$("textarea").keypress(function(e)
{
    if (e.which == 13) // ASCII newline
    {
        setTimeout(function(that)
        {
            var start = that.selectionStart;
            var v = $(that).val();
            var thisLine = "";
            var indentation = 0;
            for (var i = start-2; i >= 0 && v[i] != "\n"; i--)
            {
                thisLine = v[i] + thisLine;
            }
            for (var i = 0; i < thisLine.length && thisLine[i] == " "; i++)
            {

                indentation++;
             }
             $(that).val(v.slice(0, start) + " ".repeat(indentation) + v.slice(start));
             that.selectionStart = start+indentation;
             that.selectionEnd = start+indentation;  
}, 0.01, this);
     }
});
<textarea rows="20" cols="40"></textarea>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

最后的乘客 2024-11-09 16:58:33

环顾四周,我从未发现任何可以在所有情况下都能很好地缩进/取消缩进的东西。我下面的内容支持以下内容,并且也不依赖于 jQuery。

功能:

  • 代码中可自定义的缩进级别
  • 当按 Enter 时,它会在新行上保持缩进级别
  • 当在任意位置按 Tab 时,它将在当前行的开头缩进
  • 当按 < 时kbd>Shift + Tab 任何地方都会取消当前行的缩进

注意:这需要在 keydown 事件上发生,因为这将提供此代码与更改前的值

const editorIndentSpaces = 2;
const indent = " ".repeat(editorIndentSpaces);
const unIndentPattern = new RegExp(`^ {${editorIndentSpaces}}`);

document.querySelector("textarea")
  .addEventListener("keydown", ev => {
    const textarea = ev.target;
    const v = textarea.value;
    const startPos = textarea.selectionStart;
    const endPos = textarea.selectionEnd;
    if (ev.key === "Tab") {
      ev.preventDefault(); //stop the focus from changing
      const isUnIndenting = ev.shiftKey;

      if (startPos === endPos) {
        //nothing selected, just indent/unindent where the cursor is
        let newCursorPos;
        const lineStartPos = v.slice(0, startPos).lastIndexOf("\n") + 1;
        const lineEndPos = v.slice(lineStartPos, v.length).indexOf("/n");
        if (isUnIndenting) {
          const newLineContent = v
            .slice(lineStartPos, lineEndPos)
            .replace(unIndentPattern, "");
          textarea.value =
            v.slice(0, lineStartPos) + newLineContent + v.slice(lineEndPos);
          newCursorPos = Math.max(startPos - editorIndentSpaces, lineStartPos);
        } else {
          textarea.value =
            v.slice(0, lineStartPos) + indent + v.slice(lineStartPos);
          newCursorPos = startPos + editorIndentSpaces;
        }
        textarea.setSelectionRange(newCursorPos, newCursorPos);
      } else {
        //Indent/unindent the selected text
        const lineStartPos = v.slice(0, startPos).lastIndexOf("\n") + 1;
        const selection = v.substring(lineStartPos, endPos);
        let result = "";
        const lines = selection.split("\n");
        for (let i = 0; i < lines.length; i++) {
          if (isUnIndenting) {
            //unindent selected lines
            result += lines[i].replace(unIndentPattern, "");
          } else {
            //Indent selected lines
            result += indent + lines[i];
          }

          if (i < lines.length - 1) {
            //add line breaks after all but the last line
            result += "\n";
          }
        }

        textarea.value = v.split(selection).join(result);
        if (isUnIndenting) {
          textarea.setSelectionRange(
            Math.max(startPos - editorIndentSpaces, lineStartPos),
            lineStartPos + result.length
          );
        } else {
          textarea.setSelectionRange(
            startPos + editorIndentSpaces,
            lineStartPos + result.length
          );
        }
      }
    } else if (ev.key === "Enter") {
      //When enter is pressed, maintain the current indentation level

      //We will place the newline character manually, this stops it from being typed
      ev.preventDefault();

      //Get the current indentation level and prefix the new line with the same
      const prevLinePos = v.slice(0, startPos).lastIndexOf("\n") + 1;
      const prevLine = v.slice(prevLinePos, endPos);
      const levels = prevLine.match(/^ */)[0].length / editorIndentSpaces;
      const indentation = indent.repeat(levels);
      textarea.value =
        v.slice(0, endPos) + "\n" + indentation + v.slice(endPos);

      //Set the cursor position
      const newCursorPos = endPos + 1 + indentation.length;
      textarea.setSelectionRange(newCursorPos, newCursorPos);
    }
  });
<textarea rows="10">sample test
line2
  line3
  line4</textarea>

Looking around I never found anything that did indenting/unindenting well for all cases. What I have below supports the following, and is not dependent on jQuery either.

Features:

  • customizable indentation level in code
  • When pressing Enter it maintains the indentation level on the new line
  • When pressing Tab anywhere it will indent at the beginning of the current line
  • When pressing Shift + Tab anywhere it will un-indent the current line

NOTE: This needs to occur on a keydown event since that will provide this code with the value before it has been changed

const editorIndentSpaces = 2;
const indent = " ".repeat(editorIndentSpaces);
const unIndentPattern = new RegExp(`^ {${editorIndentSpaces}}`);

document.querySelector("textarea")
  .addEventListener("keydown", ev => {
    const textarea = ev.target;
    const v = textarea.value;
    const startPos = textarea.selectionStart;
    const endPos = textarea.selectionEnd;
    if (ev.key === "Tab") {
      ev.preventDefault(); //stop the focus from changing
      const isUnIndenting = ev.shiftKey;

      if (startPos === endPos) {
        //nothing selected, just indent/unindent where the cursor is
        let newCursorPos;
        const lineStartPos = v.slice(0, startPos).lastIndexOf("\n") + 1;
        const lineEndPos = v.slice(lineStartPos, v.length).indexOf("/n");
        if (isUnIndenting) {
          const newLineContent = v
            .slice(lineStartPos, lineEndPos)
            .replace(unIndentPattern, "");
          textarea.value =
            v.slice(0, lineStartPos) + newLineContent + v.slice(lineEndPos);
          newCursorPos = Math.max(startPos - editorIndentSpaces, lineStartPos);
        } else {
          textarea.value =
            v.slice(0, lineStartPos) + indent + v.slice(lineStartPos);
          newCursorPos = startPos + editorIndentSpaces;
        }
        textarea.setSelectionRange(newCursorPos, newCursorPos);
      } else {
        //Indent/unindent the selected text
        const lineStartPos = v.slice(0, startPos).lastIndexOf("\n") + 1;
        const selection = v.substring(lineStartPos, endPos);
        let result = "";
        const lines = selection.split("\n");
        for (let i = 0; i < lines.length; i++) {
          if (isUnIndenting) {
            //unindent selected lines
            result += lines[i].replace(unIndentPattern, "");
          } else {
            //Indent selected lines
            result += indent + lines[i];
          }

          if (i < lines.length - 1) {
            //add line breaks after all but the last line
            result += "\n";
          }
        }

        textarea.value = v.split(selection).join(result);
        if (isUnIndenting) {
          textarea.setSelectionRange(
            Math.max(startPos - editorIndentSpaces, lineStartPos),
            lineStartPos + result.length
          );
        } else {
          textarea.setSelectionRange(
            startPos + editorIndentSpaces,
            lineStartPos + result.length
          );
        }
      }
    } else if (ev.key === "Enter") {
      //When enter is pressed, maintain the current indentation level

      //We will place the newline character manually, this stops it from being typed
      ev.preventDefault();

      //Get the current indentation level and prefix the new line with the same
      const prevLinePos = v.slice(0, startPos).lastIndexOf("\n") + 1;
      const prevLine = v.slice(prevLinePos, endPos);
      const levels = prevLine.match(/^ */)[0].length / editorIndentSpaces;
      const indentation = indent.repeat(levels);
      textarea.value =
        v.slice(0, endPos) + "\n" + indentation + v.slice(endPos);

      //Set the cursor position
      const newCursorPos = endPos + 1 + indentation.length;
      textarea.setSelectionRange(newCursorPos, newCursorPos);
    }
  });
<textarea rows="10">sample test
line2
  line3
  line4</textarea>

我最近也想这样做,但发现缺点是,如果您使用 JavaScript 更改 textarea 的值,您最终会无意中破坏您的编辑历史记录(在最后一个换行符条目之后不再进行撤消)。尽管如此,我还是通过使用以下代码让它工作:

/**
 * Implements auto-indenting in a textarea so that when you hit enter the same
 * indentation as used on the previous line will be used in the new line.  Also
 * makes it so that pressing tab will add a tab character where the cursor is.
 * 
 * WARNING:  This solution clobbers edit history (undo and redo).
 * 
 * @param {HTMLTextAreaElement} textarea
 *   The textarea to auto-indent.
 */
function autoIndent(textarea) {
  textarea.addEventListener('keydown', event => {
    const isEnter = event.which === 13;
    const isTab = event.which === 9;
    if (isEnter || isTab) {
      event.preventDefault();
      const {selectionStart, value} = textarea;
      const insertion = isEnter
        ? '\n' + (value.slice(0, selectionStart).match(/(?:^|[\r\n])((?:(?=[^\r\n])[\s])*?)\S[^\r\n]*\s*$/) || [0, ''])[1]
        : '\t';
      textarea.value = value.slice(0, selectionStart) + insertion + value.slice(selectionStart);
      textarea.selectionEnd = textarea.selectionStart = selectionStart + insertion.length;
      // Attempts to scroll to the next line but will not work if indentation is extreme.
      textarea.scrollTop += textarea.clientHeight / textarea.rows;
    }
  });
}

window.addEventListener('DOMContentLoaded', () => {
  autoIndent(document.querySelector('textarea'));
});
<textarea style="width: 100%; box-sizing: border-box;" rows="10">
window.addEventListener('DOMContentLoaded', () => {
  // This is an example
  autoIndent(document.querySelector('textarea'));
});
</textarea>

目前我的要点在这里,但我希望继续努力解决编辑历史记录问题。

I recently wanted to do this as well but found that the downside is that if you use JavaScript to change the value of textarea you will end up inadvertently clobbering your edit history (no more undos past the last line break entry). Still, I did get it to work by using the following code:

/**
 * Implements auto-indenting in a textarea so that when you hit enter the same
 * indentation as used on the previous line will be used in the new line.  Also
 * makes it so that pressing tab will add a tab character where the cursor is.
 * 
 * WARNING:  This solution clobbers edit history (undo and redo).
 * 
 * @param {HTMLTextAreaElement} textarea
 *   The textarea to auto-indent.
 */
function autoIndent(textarea) {
  textarea.addEventListener('keydown', event => {
    const isEnter = event.which === 13;
    const isTab = event.which === 9;
    if (isEnter || isTab) {
      event.preventDefault();
      const {selectionStart, value} = textarea;
      const insertion = isEnter
        ? '\n' + (value.slice(0, selectionStart).match(/(?:^|[\r\n])((?:(?=[^\r\n])[\s])*?)\S[^\r\n]*\s*$/) || [0, ''])[1]
        : '\t';
      textarea.value = value.slice(0, selectionStart) + insertion + value.slice(selectionStart);
      textarea.selectionEnd = textarea.selectionStart = selectionStart + insertion.length;
      // Attempts to scroll to the next line but will not work if indentation is extreme.
      textarea.scrollTop += textarea.clientHeight / textarea.rows;
    }
  });
}

window.addEventListener('DOMContentLoaded', () => {
  autoIndent(document.querySelector('textarea'));
});
<textarea style="width: 100%; box-sizing: border-box;" rows="10">
window.addEventListener('DOMContentLoaded', () => {
  // This is an example
  autoIndent(document.querySelector('textarea'));
});
</textarea>

For now my Gist is here but I hope to continue working on it to fix the edit history issue.

浪菊怪哟 2024-11-09 16:58:33

这是大量借用的代码(感谢 stackoverflow!),并进行了一些细微的调整。第一部分只是创建一个镜像,以便您可以知道您所在的行(与您的问题并不真正相关),但它包含设置当前缩进的内容,这很重要。

$(document).keypress(function(e) {
  if (e.keyCode ==13){
    e.preventDefault();
    var start = $('textarea').get(0).selectionStart;
    var end = $('textarea').get(0).selectionEnd;
    // set textarea value to: text before caret + tab + text after caret
    var spaces = "\n" 
    for (i = 0; i < start; i++) { 
      spaces += " "
    }
   $('textarea').val($('textarea').val().substring(0, start)
      + spaces 
      + $('textarea').val().substring(end));

    // put caret at right position again
    console.log(spaces.length)
    $('textarea').get(0).selectionStart =
    $('textarea').get(0).selectionEnd = start + spaces.length;
  }
})

This is a lot of borrowed code (thanks, stackoverflow!), with some minor tweaks. The first part is just creating a mirrow, so that you can know what row you are on (not really relevant to your question), but it contains something that sets the current indentation, which is important.

$(document).keypress(function(e) {
  if (e.keyCode ==13){
    e.preventDefault();
    var start = $('textarea').get(0).selectionStart;
    var end = $('textarea').get(0).selectionEnd;
    // set textarea value to: text before caret + tab + text after caret
    var spaces = "\n" 
    for (i = 0; i < start; i++) { 
      spaces += " "
    }
   $('textarea').val($('textarea').val().substring(0, start)
      + spaces 
      + $('textarea').val().substring(end));

    // put caret at right position again
    console.log(spaces.length)
    $('textarea').get(0).selectionStart =
    $('textarea').get(0).selectionEnd = start + spaces.length;
  }
})
独木成林 2024-11-09 16:58:33

尽管这篇文章已经发布了将近六年,但您可以通过以下方式自动缩进 textarea

$("textarea").keydown(function(e)
{
    if (e.which == 9) //ASCII tab
    {
        e.preventDefault();
        var start = this.selectionStart;
        var end = this.selectionEnd;
        var v = $(this).val();
        if (start == end)
        {
            $(this).val(v.slice(0, start) + "    " + v.slice(start));
            return;
        }

        var selectedLines = [];
        var inSelection = false;
        var lineNumber = 0;
        for (var i = 0; i < v.length; i++)
        {
            if (i == start)
            {
                inSelection = true;
                selectedLines.push(lineNumber);
            }
            if (i >= end)
                inSelection = false;

            if (v[i] == "\n")
            {
                lineNumber++;
                if (inSelection)
                    selectedLines.push(lineNumber);
            }
        }
        var lines = v.split("\n");
        for (var i = 0; i < selectedLines.length; i++)
        {
            lines[selectedLines[i]] = "    " + lines[selectedLines[i]];
        }

        $(this).val(lines.join("\n"));
    }
});
$("textarea").keyup(function(e)
{
    if (e.which == 13) // ASCII newline
    {
        var start = this.selectionStart;
        var v = $(this).val();
        var thisLine = "";
        var indentation = 0;
        for (var i = start-2; i >= 0 && v[i] != "\n"; i--)
        {
            thisLine = v[i] + thisLine;
        }
        for (var i = 0; i < thisLine.length && thisLine[i] == " "; i++)
        {

            indentation++;
        }
        $(this).val(v.slice(0, start) + " ".repeat(indentation) + v.slice(start));
    }

});
<textarea rows="20" cols="40"></textarea>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

不幸的是,由于它绑定到 keyup,当您按住 Enter 时,光标将位于下一行的开头。当您释放回车键时,它只会缩进新行。这意味着如果您点击 Enter,在缩进之前会有一段延迟:
自动-缩进

Although this post is almost six years old, here's how you can auto-indent textareas:

$("textarea").keydown(function(e)
{
    if (e.which == 9) //ASCII tab
    {
        e.preventDefault();
        var start = this.selectionStart;
        var end = this.selectionEnd;
        var v = $(this).val();
        if (start == end)
        {
            $(this).val(v.slice(0, start) + "    " + v.slice(start));
            return;
        }

        var selectedLines = [];
        var inSelection = false;
        var lineNumber = 0;
        for (var i = 0; i < v.length; i++)
        {
            if (i == start)
            {
                inSelection = true;
                selectedLines.push(lineNumber);
            }
            if (i >= end)
                inSelection = false;

            if (v[i] == "\n")
            {
                lineNumber++;
                if (inSelection)
                    selectedLines.push(lineNumber);
            }
        }
        var lines = v.split("\n");
        for (var i = 0; i < selectedLines.length; i++)
        {
            lines[selectedLines[i]] = "    " + lines[selectedLines[i]];
        }

        $(this).val(lines.join("\n"));
    }
});
$("textarea").keyup(function(e)
{
    if (e.which == 13) // ASCII newline
    {
        var start = this.selectionStart;
        var v = $(this).val();
        var thisLine = "";
        var indentation = 0;
        for (var i = start-2; i >= 0 && v[i] != "\n"; i--)
        {
            thisLine = v[i] + thisLine;
        }
        for (var i = 0; i < thisLine.length && thisLine[i] == " "; i++)
        {

            indentation++;
        }
        $(this).val(v.slice(0, start) + " ".repeat(indentation) + v.slice(start));
    }

});
<textarea rows="20" cols="40"></textarea>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Unfortunately, since it's binded to keyup, while you are holding down enter, the cursor will be at the start of the next line. It will only indent a new line when you release enter. This means that if you tap enter, there will be a delay before it indents:
Auto-indent

空城之時有危險 2024-11-09 16:58:33

除了其他人的答案之外,我还想分享我根据光标位置上一行的缩进自动缩进 textarea 的尝试。

这是我的方法:

1.)检测输入事件(就像您和这里的其他人所做的那样)
2.) 获取相对于光标位置的上一行内容
3.) 获取缩进的大小
4.) 在光标位置强制插入空格/制表符

先看一下代码,最后有解释。

JavaScript 代码:

textarea.addEventListener("keyup", function(event) {
    if (event.key == "Enter") {
        // enter key is pressed in textarea
        
        // get previous line content relative to cursor position
        var line = this.value.substring(0, this.selectionStart).split("\n");
        line = line[line.length - 2];  // string
        
        // getting the indentation
        var content_to_remove = line.trimStart();  // string
        var indentation = line.replace(content_to_remove, "");
        
        // insert indentation
        this.setRangeText(indentation, this.selectionStart, this.selectionEnd, "end");
    }
});

解释

var line = this.value.substring(0, this.selectionStart).split("\n");
提取从开头到光标位置的文本,并按\n分割成一个列表。

line = line[line.length - 2]; // 字符串
获取 list 的“最后一个元素”,它是相对于当前光标位置的上一行。我不确定为什么使用 line.length - 2 而不是 line.length - 1

var content_to_remove = line.trimStart(); // 字符串
获取要删除的内容(即从开头删除所有空格,其余部分是要删除的内容)。通过获取 this ,我们可以使用 JavaScript 字符串方法将其替换为 "" ,这有助于我们获得该行的缩进:

var indentation = line.replace(content_to_remove, "");

this.setRangeText(缩进, this.selectionStart, this.selectionEnd, "结束");
然后,我们使用 setRangeText 方法将从上一行提取的缩进插入到当前行。我对这个方法不太熟悉,所以请参考这个答案


以防万一您想查看结果(我不知道如何仅在代码片段中显示 JS 部分):

  var textarea = document.getElementById("inputTextarea");
  
  // auto indent when an Enter key is pressed
  // size of indentation depends on the previous line
  /* Procedure :
      1) detect on enter event
      2) get previous line content relative to cursor position
      3) get the size of indentation
      4) force insert spaces / tabs at cursor position
  */
  textarea.addEventListener("keyup", function(event) {
    if (event.key == "Enter") {
      // enter key is pressed in textarea

      // get previous line content relative to cursor position
      var line = this.value.substring(0, this.selectionStart).split("\n");
      line = line[line.length - 2]; // string

      // getting the indentation
      var content_to_remove = line.trimStart(); // string
      var indentation = line.replace(content_to_remove, "");

      // insert indentation
      this.setRangeText(indentation, this.selectionStart, this.selectionEnd, "end");
    }
  });
textarea {
    font-size: 13px;
    /* https://stackoverflow.com/questions/47817/most-elegant-way-to-force-a-textarea-element-to-line-wrap-regardless-of-white */
    /* word-wrap: break-word;
        word-break: break-all;
        text-wrap: unrestricted; */
    /* https://stackoverflow.com/questions/424192/html-textarea-horizontal-scroll */
    white-space: nowrap;
    /* normal | nowrap */
    margin-left: 15px;
}
<!DOCTYPE html>

<html>

<head>
  <title>Test</title>
  <meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1.0">
</head>


<body>
  <textarea id="inputTextarea" rows="10" cols="50" placeholder="I Will auto-indent your code :)" autofocus>Hi
    This line got an indent
        Another indented line</textarea>
</body>


</html>

In addition to other people's answers, I would also like to share my attempt to auto-indent the textarea according to the indentation of the previous line of the cursor position.

Here is my method:

1.) Detect on-enter event ( just like what you and other people here did )
2.) Get previous line content relative to cursor position
3.) Get the size of indentation
4.) Force insert spaces / tabs at cursor position

Take a look at the code first and an explanation is provided at the end.

JavaScript code :

textarea.addEventListener("keyup", function(event) {
    if (event.key == "Enter") {
        // enter key is pressed in textarea
        
        // get previous line content relative to cursor position
        var line = this.value.substring(0, this.selectionStart).split("\n");
        line = line[line.length - 2];  // string
        
        // getting the indentation
        var content_to_remove = line.trimStart();  // string
        var indentation = line.replace(content_to_remove, "");
        
        // insert indentation
        this.setRangeText(indentation, this.selectionStart, this.selectionEnd, "end");
    }
});

Explained

var line = this.value.substring(0, this.selectionStart).split("\n");
Extract the text from the beginning to the cursor position and split it by \n into a list.

line = line[line.length - 2]; // string
Get the "last element" of the list , which is the previous line relative to current cursor position. I'm not sure why using line.length - 2 instead of line.length - 1.

var content_to_remove = line.trimStart(); // string
Getting the content to remove ( ie. remove all spaces from the beginning, the rest part is the content to remove ). By getting this , we could use JavaScript string method to replace this with "" , which help us get the indentation of the line :

var indentation = line.replace(content_to_remove, "");

this.setRangeText(indentation, this.selectionStart, this.selectionEnd, "end");
Then we insert the extracted indentation from the previous line to the current line using the setRangeText method. I'm not familiar with this method, so please kindly refer to this answer.


Just in case you want to have a look at the result ( and I don't know how to show the JS part only in the code snippet ) :

  var textarea = document.getElementById("inputTextarea");
  
  // auto indent when an Enter key is pressed
  // size of indentation depends on the previous line
  /* Procedure :
      1) detect on enter event
      2) get previous line content relative to cursor position
      3) get the size of indentation
      4) force insert spaces / tabs at cursor position
  */
  textarea.addEventListener("keyup", function(event) {
    if (event.key == "Enter") {
      // enter key is pressed in textarea

      // get previous line content relative to cursor position
      var line = this.value.substring(0, this.selectionStart).split("\n");
      line = line[line.length - 2]; // string

      // getting the indentation
      var content_to_remove = line.trimStart(); // string
      var indentation = line.replace(content_to_remove, "");

      // insert indentation
      this.setRangeText(indentation, this.selectionStart, this.selectionEnd, "end");
    }
  });
textarea {
    font-size: 13px;
    /* https://stackoverflow.com/questions/47817/most-elegant-way-to-force-a-textarea-element-to-line-wrap-regardless-of-white */
    /* word-wrap: break-word;
        word-break: break-all;
        text-wrap: unrestricted; */
    /* https://stackoverflow.com/questions/424192/html-textarea-horizontal-scroll */
    white-space: nowrap;
    /* normal | nowrap */
    margin-left: 15px;
}
<!DOCTYPE html>

<html>

<head>
  <title>Test</title>
  <meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1.0">
</head>


<body>
  <textarea id="inputTextarea" rows="10" cols="50" placeholder="I Will auto-indent your code :)" autofocus>Hi
    This line got an indent
        Another indented line</textarea>
</body>


</html>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文