一个 input 类型为 text(不是 textarea)的文本框中,光标位置的像素值。

27

更新:重复问题,详情请参见“获取输入元素的光标或文本位置(以像素为单位)”.

TL;DR - 使用非常轻量且稳定的textarea-caret-position组件库,该库现在也支持<input ype="text">。演示地址:http://jsfiddle.net/dandv/aFPA7/


有没有办法知道 HTML 文本字段中的插入符位置?

<input type='text' /> 

我希望能够基于插入符的位置在像素级别上定位(并重新定位)一个div。

注意:我不想知道在字符或


你查看过这个吗?https://dev59.com/P2w05IYBdhLWcg3w72XN - Karlth
这个问题是获取输入元素中光标或文本位置的精确副本(即,一行输入,<input type="text">),而不是获取文本框(也称为<textarea>元素)中插入符号的像素坐标。 - Dan Dascalescu
1
非常轻巧且强大的textarea-caret-position 组件库现在也支持<input type="text">,使所有现有答案都过时了。演示请见http://jsfiddle.net/dandv/aFPA7。 - Dan Dascalescu
7个回答

18

使用不可见的伪元素

你可以通过使用一个绝对定位的不可见(使用visibility而不是display)的伪元素,它具有与输入文本框相同的所有CSS属性(字体,左边框和左填充),来实现这一点。

这个非常简短且易于理解的JSFiddle是一个如何工作的起点。
在Chrome和Firefox中,它可以直接使用。 而且似乎也应该在IE9+中工作。

Internet Explorer 8(及以下版本)需要一些额外的代码才能从输入框内文本的开头获取插入符位置。我甚至在顶部添加了一个仪表图,以显示每10像素的线,以便您可以看到是否正确测量。注意,线条位于1、11、21...像素位置。

这个例子的作用实际上是将插入符位置之前的所有文本放入伪元素里面,并测量它的像素宽度。这样可以得到从文本框左侧的偏移量。

当它将文本复制到伪元素时,它还会用非断行空格替换普通空格,以便它们实际上被渲染。否则,如果您将插入符定位在空格之后,您将得到错误的位置:

var faux = $("#faux");
$("#test").on("keyup click focus", function(evt) {
    // get caret offset from start
    var off = this.selectionStart;

    // replace spaces with non-breaking space
    faux.text(this.value.substring(0, off).replace(/\s/g, "\u00a0"));
});​

注意假维度的正确尺寸

  • 填充
  • 边框
  • 外边距

已被移除以获取正确的值,否则元素会太宽。元素的框必须紧随其包含的文本之后结束。

插入符号在输入框开始位置的像素位置可以轻松获取:

faux.outerWidth();

问题

然而,有一个问题。我不确定如何处理文本框内的文本被滚动时(当太长时),插入符不是在文本的最末尾,而是在中间某个位置...如果它在末尾,那么插入符的位置总是在可能的最大位置(输入宽度减去右侧维度 - 填充、边框、外边距)。

我不确定是否可能获得文本框内文本的滚动位置?如果可以,则甚至可以解决此问题。但是我不知道任何解决方法...

希望这有所帮助。


我认为你的答案非常好。我很快就会将其标记为正确答案(只是给其他人一个回答的机会)。我暂时不打算标记它的唯一原因是,它似乎相当困难,我希望能找到更简单的解决方案。 - Zo72
@Zo72:没问题。赏金还有6天到期,期间可能会有人提出其他解决方案,尽管我怀疑是否存在其他解决方案。而且我的解决方案并不像看起来那么难。从提供的代码中可以看出,它相当简单...让我们看看其他人的想法。我也很想看看其他的解决方案。 :) - Robert Koritnik
我也有这个疑问。但是让我困扰的是,浏览器知道光标的位置(因为它在闪烁),所以原则上应该更容易得多。 - Zo72
2
@Zo72: 并不完全是这样。 浏览器的责任并非渲染闪烁的插入符号。那是操作系统 GUI 的功能。浏览器仅显示系统文本框。然后闪烁由操作系统和其 GUI 配置和功能完成。但是输入框可能仍由浏览器呈现,尽管我认为插入符号取决于操作系统功能。 - Robert Koritnik
2
有趣的解决方案加一。 - n.podbielski
3
@RobertKoritnik提到的问题可以通过使用element.scrollLeft来调整插入符号位置来解决。我已经更新了非常轻量级和稳健的textarea-caret-position Component库,以支持<input type="text">。在jsfiddle.net/dandv/aFPA7上有演示。 - Dan Dascalescu

4

在我上面的评论中提到,我想以下内容应该有效。

为了展示原理,我省略了格式化,因此元素(id="spacer")的颜色可能需要设置为#FFFFFE,可能还需要添加一些前导空间像素等... 但是现在插入符号的移动将成比例进行 - 假设 id's test 和 spacer 使用相同的字体。

当然,我们不知道距离是多少[px]如询问所述,但我们能够完全将内容定位(在这种情况下是插入符号)与比例字体的宽度相对齐,因此如果这是最终目标(如OP第3行中所提到的),那么....voi lá!

<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <script type="text/javascript">
            function adjust()
            {
                document.getElementById('spacer').innerHTML=document.getElementById('test').value;
            }
        </script>
        <p>
            <input name="test" id="test" type="text" onkeyup="adjust()" /> Test <br />
            <span id="spacer"></span>^here
        </p>
    </body>
</html>

更新:

<span id="spacer" style="color: #FFFFFF"></span>

这将使得在白色背景下间隔符不可见,但仍然不能防止用户通过选择span元素来突出显示间隔符文本。留下了使间隔符不可选的任务 - 这里有一个好的解决方案(链接)


真是个聪明的解决方案。不过你有点晚了,已经来不及了。谢谢。 - Zo72
迟做总比不做好,只要它能够正常工作。我试图关注你想要实现的目标(定位),而不是你认为需要实现它的方式(像素度量-这并不一定相同),因此我提出了一个令人惊讶的轻量级答案(在我看来)。 - MikeD
这基本上是我提供的解决方案,但功能较少。我故意让假的“div”可见,这样人们就可以看到发生了什么。我确实说过它必须被隐藏,我也可以将其定位在“input”下面。而且你的解决方案在用户输入空格时也无法工作,当他们使用箭头键重新定位插入符号时也无法工作。总之:这只是我的解决方案的一个小子集。唯一的例外是我使用了jQuery,而你使用了JavaScript + DOM直接操作。 - Robert Koritnik
1
顺便说一下:使用“visibility”隐藏您的“span”会是一个更好的选择,因为1.您可以避免背景冲突(考虑图像),以及2.额外的脚本来防止选择。不可见元素无法被选择。为什么要复杂化事情,当您可以避免它们呢。简单就是王道 - Robert Koritnik
2
我已经更新了非常轻量级和强大的textarea-caret-position 组件库,以支持<input type="text">。在jsfiddle.net/dandv/aFPA7上可以查看演示。 - Dan Dascalescu
显示剩余2条评论

2
您可以使用<span contenteditable="true"></span><input type="hidden" />。这样,您就可以使用window.getSelection()getRangeAt以及getClientRects来获取位置。这对于大多数现代浏览器都有效,包括IE9+:
function getCaret() {
    var caret = caret = {top: 0, height: 0},
        sel = window.getSelection();
    caret.top = 0;
    caret.height = 0;
    if(sel.rangeCount) {
        var range = sel.getRangeAt(0);          
        var rects = range.getClientRects();
        if (rects.length > 0) {
            caret.top = rects[0].top;
            caret.height = rects[0].height;
        } else {
            caret.top = range.startContainer.offsetTop - document.body.scrollTop;
            caret.height = range.startContainer.clientHeight;
        }
    }
    return caret;
}

(请保留HTML标签)

请注意,我从中提取的项目针对移动Safari,并使用一个大的contenteditable视图。您可能需要调整它以适应您的需求。它更多地用于演示如何获取X和Y坐标。

对于IE8及以下版本,IE有一种专有API可用于获取相同的信息。

棘手的部分是确保您的元素仅允许纯文本。特别是在粘贴事件上。当span失去焦点(onblur)时,您希望将内容复制到隐藏字段中,以便可以发布。


2

1
我已经更新了非常轻量级和强大的textarea-caret-position 组件库,以支持<input type="text">。演示请访问jsfiddle.net/dandv/aFPA7。所有旧答案现在都已过时。 - Dan Dascalescu

1

那Canvas 2D呢?它有绘制和测量文本的API。您可以使用它。设置相同的字体、字号,您应该能够从文本框中测量字符串的宽度。

      $('body').append('<canvas id="textCanvas" width="100px" height="100px">I\'m sorry your browser does not support the HTML5 canvas element.</canvas>');
      var canvas = document.getElementById('textCanvas');
      var ctx = canvas.getContext('2d');
      ctx.measureText(text);
      var width = mes.width;

我使用了这种解决方案来获取一些文本的宽度,以便在WebGL中绘制它。它运行得很好,但我从未测试过当文本对于画布上下文来说太大时会发生什么,尽管我认为这应该没问题,因为它是用于测量文本的API。但是谁知道呢,你应该完全检查一下!:)


有创造性的解决方案,但可能过于复杂。 - Andrew Rhyne
你好?我们正在讨论文本框中像素计数的解决方案。谁需要这个呢?尽管如此,我认为它比这里的任何其他解决方案都要准确得多,而且代码也不复杂。但这只是我的观点。 - n.podbielski
1
一个可靠的解决方案,用于获取光标在像素中的位置,而不需要实例化canvas对象。这个解决方案是由非常轻量级和强大的textarea-caret-position Component库实现的。我刚刚更新了它,以支持<input type="text">。演示请参见jsfiddle.net/dandv/aFPA7。 - Dan Dascalescu

0

这是一个可用的示例 http://jsfiddle.net/gPL3f/2/

为了找到插入符号的位置,我计算了输入框中的字母数量,并将该值乘以一个字母的大小。

然后将 div 移动到该位置。

<html>
<head>
<style type="text/css">
body { font-size: 12px; line-height: 12px; font-family: arial;}
#box { width: 100px; height: 15px; position: absolute; top: 35px; left: 0px; background: pink;}
</style>
<script type="text/javascript" src="jquery-1.7.1.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var letter_size = 6;
$("input[type='text']").keyup(function(e) {
    move_size = $(this).val().length * letter_size;
    $('#box').css({'left': move_size+'px'}); 
});
});
</script>

</head>
<body>
<input type="text" />
<div id="box">
/ move 
</div>
</body>
<html>

更新

$(document).ready(function(){
    var letter_size = 6;
    $("input[type='text']").keyup(function(e) {
        $('.hidden_for_width').text($(this).val());
        move_size = $('.hidden_for_width').width();
        $('#box').css({'left': move_size});
    });
});

添加了一个隐藏的容器,在其中插入输入值。然后获取该容器的宽度并相应地移动我的 DIV。


不适用于比例字体...在字段字符串中输入“my bllllllllllllllllll”,并在每次输入字母“l”(利马)后看到间隙变得越来越大。 - MikeD
一个字母的大小是多少?考虑到像 i、m、w 这样宽度差异巨大的字母。这只适用于等宽字体。 - Robert Koritnik
@MikeD:或者输入wwwwwwwww,或者mmmmmmmm,都可以。 - Robert Koritnik
确实有一些环境,其中比例字母的实际宽度以[px]为单位已知(字体类)。也许可以在_Change()中复制输入的字符串,并将其显示在下方,颜色仅略微不同于背景色,作为一个逐渐增长的占位符...(推测) - MikeD
2
我已经更新了非常轻量级和强大的textarea-caret-position 组件库,以支持<input type="text">。在jsfiddle.net/dandv/aFPA7上有演示。它支持你能想到的任何东西,无论是wwww还是iiii - Dan Dascalescu

-1
function getCursorPosition (elem) {
  var pos = 0;  
  if (document.selection) {
    elem.focus ();
    var sele = document.selection.createRange();
    sele.moveStart('character', -elem.value.length);
    pos = sele.text.length;
  }
  else if (elem.selectionStart || elem.selectionStart == '0')
    pos = elem.selectionStart;
  return pos;
}​

getCursorPosition(document.getElementById('textbox1'))

Ivlev没有告诉我像素位置。 - Zo72
5
你的最初帖子是关于光标位置的。 - Kirill Ivlev
我认为你现在应该删除你的回答。如果你不知道修改后的问题的答案,最多只能将问题投下反对票,如果你不喜欢这个变化的话。更好的方法是同时编辑回答 :) - Sami
1
@KirillIvlev 不,我的原始问题是关于像素的。 - Zo72

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接