Mr_*_*een 28 javascript jquery textarea cross-browser
在我的项目中,我试图以textarea
像素为单位获得插入符号的偏移位置.可以这样做吗?
在问这里之前,我已经经历了许多链接,尤其是Tim Down,但我找不到适用于IE8 +,Chrome和Firefox的解决方案.这似乎添下正在此.
我发现的其他 一些链接有很多问题,比如找不到插入位置的顶部偏移量.
我正在尝试获得插入符号的偏移位置,因为我想在其中显示基于插入符的偏移位置的自动完成建议框textarea
.
PS:我不能使用a,contenteditable div
因为我写了很多与a相关的代码textarea
.
paw*_*wel 17
您可以创建一个单独的(不可见)元素,并从开始到光标位置填充textarea内容.Textarea和"clone"应该具有匹配的CSS(字体属性,填充/边距/边框和宽度).然后将这些元素堆叠在一起.
让我从一个工作示例开始,然后浏览代码:http://jsfiddle.net/g7rBk/
HTML:
<textarea id="input"></textarea>
<div id="output"><span></span></div>
<div id="xy"></div>
Run Code Online (Sandbox Code Playgroud)
Textarea是不言自明的.输出是一个隐藏元素,我们将传递文本内容并进行测量.重要的是我们将使用内联元素."xy"div只是测试目的的指标.
CSS:
/* identical styling to match the dimensions and position of textarea and its "clone"
*/
#input, #output {
position:absolute;
top:0;
left:0;
font:14px/1 monospace;
padding:5px;
border:1px solid #999;
white-space:pre;
margin:0;
background:transparent;
width:300px;
max-width:300px;
}
/* make sure the textarea isn't obscured by clone */
#input {
z-index:2;
min-height:200px;
}
#output {
border-color:transparent;
}
/* hide the span visually using opacity (not display:none), so it's still measurable; make it break long words inside like textarea does. */
#output span {
opacity:0;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* the cursor position indicator */
#xy {
position:absolute;
width:4px;
height:4px;
background:#f00;
}
Run Code Online (Sandbox Code Playgroud)
JavaScript的:
/* get references to DOM nodes we'll use */
var input = document.getElementById('input'),
output = document.getElementById('output').firstChild,
position = document.getElementById('position'),
/* And finally, here it goes: */
update = function(){
/* Fill the clone with textarea content from start to the position of the caret. You may need to expand here to support older IE [1]. The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
*/
output.innerHTML = input.value.substr( 0, input.selectionStart ).replace(/\n$/,"\n\001");
/* the fun part!
We use an inline element, so getClientRects[2] will return a collection of rectangles wrapping each line of text.
We only need the position of the last rectangle.
*/
var rects = output.getClientRects(),
lastRect = rects[ rects.length - 1 ],
top = lastRect.top - input.scrollTop,
left = lastRect.left+lastRect.width;
/* position the little div and see if it matches caret position :) */
xy.style.cssText = "top: "+top+"px;left: "+left+"px";
}
Run Code Online (Sandbox Code Playgroud)
[2] https://developer.mozilla.org/en/docs/DOM/element.getClientRects
编辑:此示例仅适用于固定宽度的textarea.要使其与用户可调整大小的textarea一起使用,您需要向resize事件添加事件侦听器,并设置#output维度以匹配新的#input维度.
Mat*_*tra 16
这是一种使用rangyinputs,rangy和jQuery的方法.
它基本上将整个文本从内部复制textarea
到div
相同大小的文本中.我已经设置了一些CSS来确保在每个浏览器中,textarea
并div
以完全相同的方式包装它们的内容.
当textarea
被点击时,我读出在该字符索引插入符号被定位,然后我插入一个插入符span
内的同一索引处div
.通过这样做,我最终遇到问题,span
如果用户点击一行的开头,插入符号跳回到上一行.为了解决这个问题,我检查前一个字符是否为a space
(允许发生换行),如果是true
,我将其换成a span
,然后将下一个单词(插入位置后面的单词)换行span
.现在我比较这两者之间的最高值span
,如果它们不同,有一些包装正在进行,所以我假设它top
的left
值和#nextword
span
插入位置相等.
这种方法仍然可以改进,我敢肯定我没有想到可能出错的一切,即使我有,那么我也没有费心为所有这些方法实施修复,因为我没有现在是时候这么做了,你需要注意的一些事情:
目前,它在Windows 8上使用最新版Chrome,Firefox,IE和Safari的浏览器的工作方式完全相同.我的测试虽然不是很严格.
这是一个jsFiddle.
我希望它会对你有所帮助,至少它可能会给你一些建议.
我已经包含了一个ul
位于正确位置的for,并修复了一个Firefox问题,textarea
在DOM操作之后,选择没有重新设置回原来的位置.
我已经添加了IE7-IE9支持并修复了评论中指出的多字选择问题.
我添加了对插入的硬返回Enter和连续多个空格的支持.
我修复了ctrl+ shift+ left arrow文本选择方法的默认行为问题.
JavaScript的
function getTextAreaXandY() {
// Don't do anything if key pressed is left arrow
if (e.which == 37) return;
// Save selection start
var selection = $(this).getSelection();
var index = selection.start;
// Copy text to div
$(this).blur();
$("div").text($(this).val());
// Get current character
$(this).setSelection(index, index + 1);
currentcharacter = $(this).getSelection().text;
// Get previous character
$(this).setSelection(index - 1, index)
previouscharacter = $(this).getSelection().text;
var start, endchar;
var end = 0;
var range = rangy.createRange();
// If current or previous character is a space or a line break, find the next word and wrap it in a span
var linebreak = previouscharacter.match(/(\r\n|\n|\r)/gm) == undefined ? false : true;
if (previouscharacter == ' ' || currentcharacter == ' ' || linebreak) {
i = index + 1; // Start at the end of the current space
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;
}
// Insert `#caret` at the position of the caret
range.setStart($("div")[0].childNodes[0], index);
var caret = $("<span id='caret'></span>")[0];
range.insertNode(caret);
var carettop = $("#caret").position().top;
// If preceding character is a space, wrap it in a span
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;
}
// Set textarea selection back to selection start
$(this).focus();
$(this).setSelection(index, selection.end);
// If the top value of the previous character span is not equal to the top value of the next word,
// there must have been some wrapping going on, the previous character was a space, so the wrapping
// would have occured after this space, its safe to assume that the left and top value of `#nextword`
// indicate the caret position
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');
}
// if not, then there was no wrapping, we can take the left and the top value from `#caret`
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);
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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 {
/* Firefox wrapping fix */
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
/* IE8/IE9 wrapping fix */
padding-right: 5px\0/;
width: 295px\0/;
}
span#caret
{
display: inline-block\0/;
}
Run Code Online (Sandbox Code Playgroud)
有一个更简单的解决方案,以像素为单位获得插入位置,而不是在其他答案中提供.
请注意,这个问题与2008年的问题重复,我在这里已经回答了.我只会在那个链接上保留答案,因为这个问题应该在几年前就已经关闭了.
我已经为meteor-autocomplete寻找了textarea插入符号插件,所以我已经评估了GitHub上的所有8个插件.到目前为止,胜利者是来自Component的textarea-caret-position.
<div>
屏幕外创建一面镜子,风格与之完全相同<textarea>
.然后,直到插入符号的textarea的文本被复制到div中,并且<span>
在它之后插入a .然后,跨度的文本内容被设置为textarea中文本的剩余部分,以便忠实地再现人造div中的包装.
这是保证处理与包装长行相关的所有边缘情况的唯一方法.它也被GitHub用来确定其@用户下拉列表的位置.
归档时间: |
|
查看次数: |
18559 次 |
最近记录: |