5

I need to find the position of the character in the input field based on the mouse movement.

for example: if the input field has value as 'abcde' and I am moving my mouse on the character 'a' then the position will be 1, 'b' will provide 2 and so on for the other characters.

I did find few examples that works with the selectionStart etc., but they work only when the caret is placed at some position and then find the placed position.

What I am looking for is - that there may / may not be click done in the input field, regardless when the mouse is moving on the characters - I be able to know the position of the character, where mouse hovered upon.

I haven't come across any idea how I can convert the mouse coordinates respectively into character position. Are there any such javascript API's?

Any help / pointers / ideas are much appreciated.

Update

My end goal is automatically move the caret as the drag is happening over the contenteditable div. I found a following link asking for the similar problem - but there is no solution suggested so far.

caret auto move - https://stackoverflow.com/questions/12397792/update-caret-position-of-input-and-insert-text-on-drop-event/53660415#53660415

I have also added another example to show the different behavior of native drag vs. jQuery drag. I am using jQuery draggable that does not support auto drag of caret.

https://jsfiddle.net/VidushiGupta/7warczfy/

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
  • So apparently you may be able to get mouse pointer location by registering maybe handler on the text node but you probably cant get exact location of individual characters because not all chars are same in size. For example character `l` or `i` and `w` have different widths. – Rikin Dec 06 '18 at 00:43
  • 1
    I think that there's nothing for what you are looking for directly because you would be working with just a text node. You could break the text into `span` (or other inline tag) for each character, and control the `mouseenter` and `mouseleave`, but that sounds overcomplicating things (and it could be too _expensive_, especially if it is a long text). Maybe if you use a monospace font, you could calculate the position by getting the mouse coordinates within the container. – Alvaro Montoro Dec 06 '18 at 02:05
  • please add what you have tried so far – Rachel Gallen Dec 06 '18 at 02:51

2 Answers2

2

Jquery: You could try the Caret plugin (I'm not sure this meets your needs though) You can read the jquery page, but it's not very informative!

Javascript solutions:

I looked around for pre-existing solutions but regarding the hover/mouseover found only one potential adaptable solution. This code is not mine, it's adapted from a public fiddle I found by Carlos Delgado (I adapted so that if you hover on the "Show position" text, it will display the cursor position). It also demonstrates the use of selectionStart and selectionEnd, so start and end positions are displayed if text is selected. If you move the cursor forward or back with the arrow keys, and then hover again, the updated cursor position will show.

Also, here's a shorter [alternative] version (without the start/end etc)

function getInputSelection(el) {
  var start = 0,
    end = 0,
    normalizedValue, range,
    textInputRange, len, endRange;

  if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
    start = el.selectionStart;
    end = el.selectionEnd;
  } else {
    range = document.selection.createRange();

    if (range && range.parentElement() == el) {
      len = el.value.length;
      normalizedValue = el.value.replace(/\r\n/g, "\n");

      // Create a working TextRange that lives only in the input
      textInputRange = el.createTextRange();
      textInputRange.moveToBookmark(range.getBookmark());

      // Check if the start and end of the selection are at the very end
      // of the input, since moveStart/moveEnd doesn't return what we want
      // in those cases
      endRange = el.createTextRange();
      endRange.collapse(false);

      if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
        start = end = len;
      } else {
        start = -textInputRange.moveStart("character", -len);
        start += normalizedValue.slice(0, start).split("\n").length - 1;

        if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
          end = len;
        } else {
          end = -textInputRange.moveEnd("character", -len);
          end += normalizedValue.slice(0, end).split("\n").length - 1;
        }
      }
    }
  }

  return {
    start: start,
    end: end
  };
}

document.getElementById("trigger").addEventListener("mouseover", function() {
  var input = document.getElementById("texto");
  var inputContent = input.value.length;
  // You may want to focus the textbox in case it's not
  input.focus();
  var result = getInputSelection(input);
  var resultSpan = document.getElementById("result");

  if (result.start == result.end) {
    resultSpan.innerHTML = "No text selected. Position of cursor is " + result.start + " of " + inputContent;
  } else {
    resultSpan.innerHTML = "You've selected text (" + result.start + " to " + result.end + ") from a total length of " + inputContent;
  }

}, false);
#trigger{background:#ff8890;}
<p>
  Get the cursor position in a textarea or a text input or the selected text.
</p><br>
<textarea id="texto"></textarea><br><br>
<input type="text" id="trigger" value="Show Position" size="10" /><br><br>
<span id="result"></span>

This solution may also work: It shows the position in the console (perhaps that's preferable?). You could adjust that if needsbe.

document.getElementById('showpos').addEventListener('mouseenter', e => {
  console.log('Caret at: ', e.target.selectionStart)
})
<input id="showpos" />

Hope these ideas help

Rachel

Rachel Gallen
  • 27,943
  • 21
  • 72
  • 81
  • Thanks for your help - but this still does not solve the concern that I have. – Vidushi Gupta Dec 06 '18 at 20:48
  • 1
    The 1st mentioned solution picks the position of the caret from the text box. The use case I am looking for is that when characters are hovered over, there be a way to find the position. For example caret might be placed at the end of the string "abcde", but the mouse hovers over "b" and I be able to get the position 2. – Vidushi Gupta Dec 06 '18 at 20:57
  • @VidushiGupta Did you try the other answers? They are not all the same. – Rachel Gallen Dec 06 '18 at 20:59
  • 1
    The reason I need to do this is to achieve the feature supported by native HTML draggable where when you move the dragged item - the respective caret moves along. This is missing in jQuery draggable lib - Thus I am trying to acheive the same. inhttps://jsfiddle.net/VidushiGupta/7warczfy/ There are two examples. I have methods that can set the caret in particular position - but I need to know the position when I move my dragged element. – Vidushi Gupta Dec 06 '18 at 21:01
  • I have tried googled and tried to find any pointer, that can even get me started and give ideas - but have not came across any thing yet :( – Vidushi Gupta Dec 06 '18 at 21:02
  • 1
    Rachel - I tried all solutions that you mentioned - they all are based on the fact that caret is already at a particular position, and we can get the position number. I am looking for something where I just do a mouse hover on the text character and can get the position :( – Vidushi Gupta Dec 06 '18 at 21:07
  • I added quite a few pointers, and you added no code to your question, so I had no idea it had anything to with draggable. I am relaxing after a very tough day at the moment. Ask a more specific question and include your draggable code to illustrate the problem. Upvotes cost nothing btw. – Rachel Gallen Dec 06 '18 at 21:07
  • https://jsfiddle.net/VidushiGupta/7warczfy/ is something that might give an idea. Rachel - Thanks for helping out though.. – Vidushi Gupta Dec 06 '18 at 21:11
  • "Upvotes cost nothing btw." I'm having the same problem as Vidushi and your answer doesn't solve the problem. Perhaps this could be solved if we iterate over every valid caret position and return the one with the shortest distance to the x,y coordinates. But this will not be performant for large textboxes. – EricP Dec 14 '22 at 23:14
0

Try this function, giving it the mouse move event's pageX and pageY values:

/**
 * Get the closest caret position to the given page coordinates.
 * @param x {int}
 * @param y {int}
 * @param doc {Document}
 * @return {[Node, int]} Node with the caret and offset within that node. */
function fromCoordinates(x, y, doc=document) {

    // Firefox
    if (doc.caretPositionFromPoint) {
        let caretPosition = doc.caretPositionFromPoint(x, y);
        if (caretPosition)
            return [caretPosition.offsetNode, caretPosition.offset];
    }

    // All others
    if (doc.caretRangeFromPoint) {
        let range = doc.caretRangeFromPoint(x, y);
        if (range)
            return [range.startContainer, range.startOffset];
    }

    return null;
}
EricP
  • 3,395
  • 3
  • 33
  • 46