这里有一种用 rangyinputs、rangy 和 jQuery 的方法。
这个方法基本上是将textarea
中的整个文本复制到相同大小的div
中。我设置了一些 CSS,以确保在每个浏览器中,textarea
和div
以完全相同的方式包裹它们的内容。
当单击
textarea
时,我会读取光标所在的字符索引,然后在
div
内部插入一个与该索引相同的光标
span
。只做这个操作时,如果用户单击行首,光标
span
会跳回到上一行,因此我检查前一个字符是否是一个空格(这将允许换行),如果是,则将其包装在一个
span
中,并将下一个单词(紧接着光标位置的单词)也包装在一个
span
中。现在,我比较这两个
span
的顶部值,如果它们不同,则发生了一些换行,因此我假设
#nextword
span
的
top
和
left
值等同于光标位置。
这种方法仍有改进的空间,我相信我没有考虑到所有可能出现的问题,即使我已经考虑到了,我也没有为所有问题实施修复,因为我目前没有时间这样做,以下是您需要考虑的一些事情:
它还不能处理使用Enter插入的硬回车(已解决)
输入连续多个空格时位置会断开(已解决)
- 我认为连字符也可以允许内容换行。
目前在Windows 8上,使用Chrome、Firefox、IE和Safari的最新版本,它在所有浏览器中的工作方式完全相同。我的测试还不够严格。
这里有一个jsFiddle。
我希望它能对您有所帮助,至少它可能会给您一些构建的思路。
一些特点:
我已经为您添加了一个ul
,它位于正确的位置,并修复了Firefox中的一个问题,在DOM操作后,textarea
选择未重新设置回其原始位置。
我已添加了IE7 - IE9支持,并修复了评论中指出的多个单词选择问题。
我已添加了对使用Enter插入硬回车和多个连续空格的支持。
我已修复了ctrl+shift+left arrow文本选择方法的默认行为问题。
JavaScript
function getTextAreaXandY() {
if (e.which == 37) return;
var selection = $(this).getSelection();
var index = selection.start;
$(this).blur();
$("div").text($(this).val());
$(this).setSelection(index, index + 1);
currentcharacter = $(this).getSelection().text;
$(this).setSelection(index - 1, index)
previouscharacter = $(this).getSelection().text;
var start, endchar;
var end = 0;
var range = rangy.createRange();
var linebreak = previouscharacter.match(/(\r\n|\n|\r)/gm) == undefined ? false : true;
if (previouscharacter == ' ' || currentcharacter == ' ' || linebreak) {
i = index + 1;
while (endchar != ' ' && end < $(this).val().length) {
i++;
$(this).setSelection(i, i + 1)
var sel = $(this).getSelection();
endchar = sel.text;
end = sel.start;
}
range.setStart($("div")[0].childNodes[0], index);
range.setEnd($("div")[0].childNodes[0], end);
var nextword = range.toHtml();
range.deleteContents();
var position = $("<span id='nextword'>" + nextword + "</span>")[0];
range.insertNode(position);
var nextwordtop = $("#nextword").position().top;
}
range.setStart($("div")[0].childNodes[0], index);
var caret = $("<span id='caret'></span>")[0];
range.insertNode(caret);
var carettop = $("#caret").position().top;
if (previouscharacter == ' ') {
range.setStart($("div")[0].childNodes[0], index - 1);
range.setEnd($("div")[0].childNodes[0], index);
var prevchar = $("<span id='prevchar'></span>")[0];
range.insertNode(prevchar);
var prevchartop = $("#prevchar").position().top;
}
$(this).focus();
$(this).setSelection(index, selection.end);
if (prevchartop != undefined && prevchartop != nextwordtop) {
$("label").text('X: ' + $("#nextword").position().left + 'px, Y: ' + $("#nextword").position().top);
$('ul').css('left', ($("#nextword").position().left) + 'px');
$('ul').css('top', ($("#nextword").position().top + 13) + 'px');
}
else {
$("label").text('X: ' + $("#caret").position().left + 'px, Y: ' + $("#caret").position().top);
$('ul').css('left', ($("#caret").position().left) + 'px');
$('ul').css('top', ($("#caret").position().top + 14) + 'px');
}
$('ul').css('display', 'block');
}
$("textarea").click(getTextAreaXandY);
$("textarea").keyup(getTextAreaXandY);
HTML
<div></div>
<textarea>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</textarea>
<label></label>
<ul>
<li>Why don't you type this..</li>
</ul>
CSS
body {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
}
textarea, div {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
width: 300px;
display: block;
overflow: hidden;
border: 1px solid black;
padding: 0;
margin: 0;
resize: none;
min-height: 300px;
position: absolute;
-moz-box-sizing: border-box;
white-space: pre-wrap;
}
span {
display: inline-block;
height: 14px;
position: relative;
}
span#caret {
display: inline;
}
label {
display: block;
margin-left: 320px;
}
ul {
padding: 0px;
margin: 9px;
position: absolute;
z-index: 999;
border: 1px solid #000;
background-color: #FFF;
list-style-type:none;
display: none;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
span {
white-space: pre-wrap;
}
}
div {
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
padding-right: 5px\0/;
width: 295px\0/;
}
span#caret
{
display: inline-block\0/;
}