297

How can I get the caret position from within an input field?

I have found a few bits and pieces via Google, but nothing bullet proof.

Basically something like a jQuery plugin would be ideal, so I could simply do

$("#myinput").caretPosition()
Sebastian Zartner
  • 18,808
  • 10
  • 90
  • 132
MarkB29
  • 4,752
  • 4
  • 21
  • 15
  • 3
    Try searching for 'cursor position', that'll give you a lot more hits, as well as some topics about this on SO. – Alec May 24 '10 at 13:11
  • 2
    possible duplicate of [How to get cursor position in textarea?](http://stackoverflow.com/questions/263743/how-to-get-cursor-position-in-textarea). See also: [FieldSelection plugin](http://laboratorium.0xab.cd/jquery/fieldselection/0.1.0/test.html) – Christian C. Salvadó May 24 '10 at 14:35
  • 2
    @CMS Finding the position in an `` is way simpler than doing it in a ` – Andrew Mao Mar 20 '14 at 17:16
  • 1
    @AndrewMao: and way harder if the text is scrolled and the caret is past `size` characters. – Dan Dascalescu Mar 22 '14 at 02:52
  • @alec: I agree searching for cursor instead of caret may yield more result. As pointed out elsewhere, I learned that _caret_ is the more appropriate term. A _cursor_ represents a location in anything while a _caret_ represents a location specifically in text. – Suncat2000 Apr 21 '20 at 13:45

10 Answers10

303

Easier update:

Use field.selectionStart example in this answer.

Thanks to @commonSenseCode for pointing this out.


Old answer:

Found this solution. Not jquery based but there is no problem to integrate it to jquery:

/*
** Returns the caret (cursor) position of the specified text field (oField).
** Return value range is 0-oField.value.length.
*/
function doGetCaretPosition (oField) {

  // Initialize
  var iCaretPos = 0;

  // IE Support
  if (document.selection) {

    // Set focus on the element
    oField.focus();

    // To get cursor position, get empty selection range
    var oSel = document.selection.createRange();

    // Move selection start to 0 position
    oSel.moveStart('character', -oField.value.length);

    // The caret position is selection length
    iCaretPos = oSel.text.length;
  }

  // Firefox support
  else if (oField.selectionStart || oField.selectionStart == '0')
    iCaretPos = oField.selectionDirection=='backward' ? oField.selectionStart : oField.selectionEnd;

  // Return results
  return iCaretPos;
}
YakovL
  • 7,557
  • 12
  • 62
  • 102
bezmax
  • 25,562
  • 10
  • 53
  • 84
  • 9
    `else if (oField.selectionStart || oField.selectionStart == '0')` could be `else if (typeof oField.selectionStart==='number')` – user2428118 Oct 18 '13 at 20:57
  • What is the idea of "oField.focus()"? It works for me without this line. Be careful if you use blur event on your input and execute that function inside a callback. – Kirill Reznikov Sep 14 '15 at 09:35
  • Are you testing that on IE? That whole if section is executed only on IE. In IE there is only a global selection, that's why it's `document.selection`, not `field.selection` or something. Also, it was possible in IE 7 (don't know if it is still possible in 8+) to select something, and then TAB out of the field without loosing selection. This way, when the text is selected but the field is not focused, `document.selection` returns zero selection. That's why, as a workaround for this bug, you have to focus on the element before reading the `document.selection`. – bezmax Sep 14 '15 at 11:40
  • always getting 0 for chrome and firefox – Kaushik Thanki Jun 26 '17 at 14:02
224

Use selectionStart. It is compatible with all major browsers.

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

This works only when no type is defined or type="text" or type="textarea" on the input.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
CommonSenseCode
  • 23,522
  • 33
  • 131
  • 186
  • 4
    If I change position using mouse, it isn't printed in the console. Is there a way to fix it? – Eugene Barsky Aug 27 '18 at 21:43
  • 6
    @EugeneBarsky Just add a new event listener for the click event. You can check the `.selectionStart` property at any time (`document.getElementById('foobar').selectionStart`), it doesn't have to be inside an event listener. – JJJ Aug 27 '18 at 21:46
  • 6
    awesome, but doesn't work in Firefox or Chrome when the input type is number. – Adam R. Grey Sep 13 '18 at 14:41
  • 1
    @JJJ Property 'selectionStart' does not exist on type 'HTMLElement'. – MojioMS Nov 13 '20 at 10:02
  • @MojioMS this does exist as part of HTMLInputElement which as has been point out will work if the target is of type text or type textarea – Sam Stephenson Jun 23 '21 at 09:58
  • 3
    As with other answers on this page, if you begin highlighting a selection and move the cursor to the end of the selection, this gives the wrong value. Selection start and selection end aren't the same thing as the cursor position. The cursor could be at one end or the other. – ggorlen Jul 08 '21 at 18:10
119

I've wrapped the functionality in bezmax's answer into jQuery if anyone wants to use it.

(function($) {
    $.fn.getCursorPosition = function() {
        var input = this.get(0);
        if (!input) return; // No (input) element found
        if ('selectionStart' in input) {
            // Standard-compliant browsers
            return input.selectionStart;
        } else if (document.selection) {
            // IE
            input.focus();
            var sel = document.selection.createRange();
            var selLen = document.selection.createRange().text.length;
            sel.moveStart('character', -input.value.length);
            return sel.text.length - selLen;
        }
    }
})(jQuery);
ggorlen
  • 44,755
  • 7
  • 76
  • 106
MarkB29
  • 4,752
  • 4
  • 21
  • 15
  • 3
    Isn't `input = $(this).get(0)` the same as `input = this`? – Mic Jun 22 '11 at 07:57
  • 5
    @Mic no, not in a jQuery plugin. In a plugin `this` refers to a full wrapped set. His code is still wrong though, it should just be `this.get(0)`. His code probably still worked because re-wrapping a wrapped set does nothing. – CatDadCode Oct 12 '11 at 20:32
  • 1
    This gives me wrong information. I was looking at cursor positions when entertaing text. My fiddle demonstrating this is: http://jsfiddle.net/fallenreaper/TSwyk/ – Fallenreaper Mar 06 '13 at 20:27
  • 1
    Firefox generates an NS_ERROR_FAILURE on input.selectionStart when the input is of number type, wrap it in a try {} catch {}? – niall.campbell May 24 '14 at 23:39
  • would be nice for the new bees if you add the usage for the function – Muhammad Omer Aslam Aug 17 '19 at 13:32
27

Got a very simple solution. Try the following code with verified result-

<html>
<head>
<script>
    function f1(el) {
    var val = el.value;
    alert(val.slice(0, el.selectionStart).length);
}
</script>
</head>
<body>
<input type=text id=t1 value=abcd>
    <button onclick="f1(document.getElementById('t1'))">check position</button>
</body>
</html>

I'm giving you the fiddle_demo

Rajesh Paul
  • 6,793
  • 6
  • 40
  • 57
  • 19
    `slice` is a relatively expensive operation and it adds nothing to this 'solution' -- `el.selectionStart` is equivalent to the length of your slice; just return that. Moreover, the reason the other solutions are more complicated is because they deal with other browsers that don't support `selectionStart`. – mpen Oct 01 '13 at 20:23
  • 3
    I've quit jobs where I had to work with code with variable names like this. – Michael Scheper Oct 16 '15 at 04:38
  • 2
    @Michael Scheper - You mean 'el' for element and 'val' for value? Those are pretty common... – user2782001 Feb 05 '16 at 02:22
  • 14
    @user2782001: I misspoke—my main concern was the function name. `f1` is about as meaningful as 'user2782001'. – Michael Scheper Feb 25 '16 at 20:17
17

There is now a nice plugin for this: The Caret Plugin

Then you can get the position using $("#myTextBox").caret() or set it via $("#myTextBox").caret(position)

YakovL
  • 7,557
  • 12
  • 62
  • 102
Jens Mikkelsen
  • 2,712
  • 19
  • 23
14
   (function($) {
    $.fn.getCursorPosition = function() {
        var input = this.get(0);
        if (!input) return; // No (input) element found
        if (document.selection) {
            // IE
           input.focus();
        }
        return 'selectionStart' in input ? input.selectionStart:'' || Math.abs(document.selection.createRange().moveStart('character', -input.value.length));
     }
   })(jQuery);
George
  • 141
  • 1
  • 3
14

There are a few good answers posted here, but I think you can simplify your code and skip the check for inputElement.selectionStart support: it is not supported only on IE8 and earlier (see documentation) which represents less than 1% of the current browser usage.

var input = document.getElementById('myinput'); // or $('#myinput')[0]
var caretPos = input.selectionStart;

// and if you want to know if there is a selection or not inside your input:

if (input.selectionStart != input.selectionEnd)
{
    var selectionValue =
    input.value.substring(input.selectionStart, input.selectionEnd);
}
pmrotule
  • 9,065
  • 4
  • 50
  • 58
2

Perhaps you need a selected range in addition to cursor position. Here is a simple function, you don't even need jQuery:

function caretPosition(input) {
    var start = input[0].selectionStart,
        end = input[0].selectionEnd,
        diff = end - start;

    if (start >= 0 && start == end) {
        // do cursor position actions, example:
        console.log('Cursor Position: ' + start);
    } else if (start >= 0) {
        // do ranged select actions, example:
        console.log('Cursor Position: ' + start + ' to ' + end + ' (' + diff + ' selected chars)');
    }
}

Let's say you wanna call it on an input whenever it changes or mouse moves cursor position (in this case we are using jQuery .on()). For performance reasons, it may be a good idea to add setTimeout() or something like Underscores _debounce() if events are pouring in:

$('input[type="text"]').on('keyup mouseup mouseleave', function() {
    caretPosition($(this));
});

Here is a fiddle if you wanna try it out: https://jsfiddle.net/Dhaupin/91189tq7/

dhaupin
  • 1,613
  • 2
  • 21
  • 24
0

const inpT = document.getElementById("text-box");
const inpC = document.getElementById("text-box-content");
// swch gets  inputs .
var swch;
// swch  if corsur is active in inputs defaulte is false .
var isSelect = false;

var crnselect;
// on focus
function setSwitch(e) {
  swch = e;
  isSelect = true;
  console.log("set Switch: " + isSelect);
}
// on click ev
function setEmoji() {
  if (isSelect) {
    console.log("emoji added :)");
    swch.value += ":)";
    swch.setSelectionRange(2,2 );
    isSelect = true;
  }

}
// on not selected on input . 
function onout() {
  // الافنت  اون كي اب 
  crnselect = inpC.selectionStart;
  
  // return input select not active after 200 ms .
  var len = swch.value.length;
  setTimeout(() => {
   (len == swch.value.length)? isSelect = false:isSelect = true;
  }, 200);
}
<h1> Try it !</h1>
    
  <input type="text" onfocus = "setSwitch(this)" onfocusout = "onout()" id="text-box" size="20" value="title">
  <input type="text" onfocus = "setSwitch(this)"  onfocusout = "onout()"  id="text-box-content" size="20" value="content">
<button onclick="setEmoji()">emogi :) </button>
Omar bakhsh
  • 896
  • 11
  • 18
0

The solution is .selectionStart:

var input = document.getElementById('yourINPUTid');
input.selectionEnd = input.selectionStart = yourDESIREDposition;
input.focus();

If .selectionEnd is not assiged, some text (S-->E) will be selected.

.focus() is required when the focus is lost; when you trigger your code (onClick).

I only tested this in Chrome.

If you want more complicated solutions, you have to read the other answers.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
hermanVG
  • 1
  • 1