如何为 HTML 文本区域添加自动缩进?
我有 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我修改了 Leo 的答案来解决延迟问题(通过使用 keypress 而不是带有 setTimeout 的 keyup),以及导致编辑文本中间不起作用的错误。
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.
环顾四周,我从未发现任何可以在所有情况下都能很好地缩进/取消缩进的东西。我下面的内容支持以下内容,并且也不依赖于 jQuery。
功能:
注意:这需要在
keydown
事件上发生,因为这将提供此代码与更改前的值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:
NOTE: This needs to occur on a
keydown
event since that will provide this code with the value before it has been changed我最近也想这样做,但发现缺点是,如果您使用 JavaScript 更改 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:
For now my Gist is here but I hope to continue working on it to fix the edit history issue.
看看这个:http://thelackthereof.org/JQuery_Autoindent
还有http://plugins.jquery.com/content/optional-auto-indent-setting
Check this out : http://thelackthereof.org/JQuery_Autoindent
and also, http://plugins.jquery.com/content/optional-auto-indent-setting
这是大量借用的代码(感谢 stackoverflow!),并进行了一些细微的调整。第一部分只是创建一个镜像,以便您可以知道您所在的行(与您的问题并不真正相关),但它包含设置当前缩进的内容,这很重要。
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.
尽管这篇文章已经发布了将近六年,但您可以通过以下方式自动缩进
textarea
:不幸的是,由于它绑定到
keyup
,当您按住 Enter 时,光标将位于下一行的开头。当您释放回车键时,它只会缩进新行。这意味着如果您点击 Enter,在缩进之前会有一段延迟:Although this post is almost six years old, here's how you can auto-indent
textarea
s: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:除了其他人的答案之外,我还想分享我根据光标位置上一行的缩进自动缩进
textarea
的尝试。这是我的方法:
1.)检测输入事件(就像您和这里的其他人所做的那样)
2.) 获取相对于光标位置的上一行内容
3.) 获取缩进的大小
4.) 在光标位置强制插入空格/制表符
先看一下代码,最后有解释。
JavaScript 代码:
解释
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 部分):
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 :
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 ofline.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 ) :