【问题标题】:How to add autoindent to HTML textarea?如何将自动缩进添加到 HTML 文本区域?
【发布时间】:2011-04-21 12:13:48
【问题描述】:

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

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

在情况(b)中,如下代码中的cmets所示,文本被正确修改,但是如果导致光标离开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>

【问题讨论】:

    标签: javascript textarea indentation


    【解决方案1】:

    我已经修改了 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>

    【讨论】:

      【解决方案2】:

      【讨论】:

      • 谢谢。不幸的是,thelackthereof.org/JQuery_Autoindent 正是我所描述的问题——由于一系列 NEWLINE 键事件(至少在 FF3 中),当光标移出可见区域时,没有垂直滚动。 AFAICT 这是由于在此解决方案中默认处理程序被抑制(使用 e.preventDefault())。
      • 我不明白你发帖plugins.jquery.com/content/optional-auto-indent-setting 的意思,AFAICT 这是一个功能请求,而不是解决方案。
      【解决方案3】:

      这是大量借用的代码(感谢 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;
        }
      })
      

      【讨论】:

      • 除非我遗漏了什么,否则这个答案的逻辑是错误的。如果您在第一行输入&lt;html&gt; 之类的内容并按Enter,您的代码会添加一个新行和六个空格(因为start 是6)。然后输入 &lt;body&gt; 并按 Enter。这增加了一个新行和 16 个空格。它需要在代码从上一行开始之前找到空格数,然后添加空格。
      【解决方案4】:

      虽然这篇文章已经有将近六年的历史了,但您可以通过以下方式自动缩进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>

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

      【讨论】:

      • 你需要设置 selectionStart 和 selectionEnd,否则光标移动到末尾(至少在 Chrome 中)。此外,如果您快速键入,有时由于延迟,它不会缩进。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-01
      • 2022-01-13
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 2012-04-27
      相关资源
      最近更新 更多