如何将自动添加到HTML textarea?

时间:2011-04-21 12:13:49

标签: javascript textarea indentation

我有HTML textarea。我想修改它,以便它实现自动缩进,即插入NEWLINE后,我想在新行的开头自动插入空格(空格数取决于前一行的缩进)。我想通过注册一个听'keypress'事件的处理程序来做到这一点。现在我有一个选择:(a)保留默认处理程序并在浏览器将换行符添加到textarea.value之后插入空格,或者(b)使用preventDefault()并自己插入整个内容(即换行符和空格)。

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

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

如何在不丢失默认滚动的情况下实现自动缩进效果?

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

谢谢!

格雷格

PS:我对整合复杂的textarea替代品不感兴趣,例如: 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>

4 个答案:

答案 0 :(得分:2)

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

&#13;
&#13;
$("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);
     }
});
&#13;
<textarea rows="20" cols="40"></textarea>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

答案 2 :(得分:0)

这是很多借来的代码(谢谢,stackoverflow!),还有一些小的调整。第一部分只是创建一个mirrow,这样你就可以知道你在哪一行(与你的问题没有关系),但它包含了设置当前缩进的东西,这很重要。

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

答案 3 :(得分:0)

虽然这篇文章已有近六年的历史,但您可以通过以下方式自动缩进textarea

&#13;
&#13;
$("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));
    }

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

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