Caret position in pixels in an input type text (not a textarea) [duplicate]

这一生的挚爱 提交于 2019-11-27 19:21:57
Robert Koritnik

Using an invisible faux element

You can accomplish this by using an absolutely positioned invisible (using visibility not display) faux element that has all the same CSS properties as your input text box (fonts, left borders and left padding).

This very short and easy to understand JSFiddle is a starting point how this script should be working.
It works in Chrome and Firefox as is. And it seems it should be working in IE9+ as well.

Internet Explorer 8 (and down) would need some additional code to get caret position from start of text within input text box. I've even added a meter at the top to show a line every 10 pixels so you can see whether it measures correctly or not. Mind that lines are at 1, 11, 21,... pixel positions.

What this example does it actually takes all the text in text box up to caret position and puts it inside the faux element and then measures its width in pixels. This gets you offset from left of text box.

When it copies text to faux element it also replaces normal spaces with non-breaking ones so they actually get rendered otherwise if you'd position caret right after space you'd get wrong position:

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"));
});​

Mind that faux's right dimensions

  • padding
  • border
  • margin

have been removed in order to get correct value, otherwise element would be too wide. Element's box has to end right after the text it contains.

Caret's position in pixels from start of input box is then easily gotten from:

faux.outerWidth();

The problem

There is one problem though. I'm not sure how to handle situation when text within text box is scrolled (when too long) and caret isn't at the very end of text but somewhere in between... If it's at the end then caret position is always at maximum position possible (input width less right dimensions - padding, border, margin).

I'm not sure if it's possible to get text scroll position within text box at all? If you can then even this problem can be solved. But I'm not aware of any solution to this...

Hope this helps.

MikeD

Elaborating on my comment above, I guess the following should work.

To show the principle I omitted formatting, so element (id="spacer") color may needed to be set to #FFFFFE, maybe some pixels of leading space added etc ... but the carret will move proportionally now - provided that id's test and spacer use the same font.

Of course we don't know the distance in [px] as asked, but we are able to position content (a carret in this case) fully in line with the width of a proportional font, so if that's the ultimate goal (like mentioned in OP line 3), then .... 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>

Update:

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

this will make the spacer invisible on a white background, but does not yet prevent the user from highlighting the spacer text by selecting the span element. Leaves the task of making the spacer unselectable - there's a good solution here

What about canvas 2D? It has API for drawing an measuring text. You could use that. Set same font, font-size and you should be able to measure width of string from text box.

      $('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;

I used this kind of solution to acquire width of some text to draw it in WebGL. I worked just fine but I never tested this what going to happen when text is to big for canvas context, thought i think it will be fine because it is API for measuring text. But ho knows you should totally check it out! :)

You could use <span contenteditable="true"></span><input type="hidden" />. That way you can use window.getSelection() with getRangeAt and getClientRects to get the position. This should work with most modern browsers, including 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;
}

(Keep in mind the project I pulled this from is targeting Mobile Safari, and uses one large contenteditable view. You may need to tweak it to fit your needs. It's more for demonstration purposes how to get the X and Y coordinates.)

For IE8 and below IE has a proprietary API for getting the same information.

The tricky part is making sure that your element only allows plain text. Specifically with the paste event. When the span looses focus (onblur) you want to copy the contents into the hidden field so it can be posted.

heres a working example http://jsfiddle.net/gPL3f/2/

in order to find the position of the caret i counted the letters inside the input and the multiplied that value with the size of 1 letter.

and after that moved the div to that location.

<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>

UPDATE

$(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});
    });
});

added a hidden container in wich i insert the value from input . after that i get the width of that container and move my DIV accordingly

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'))
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!