234

Does anybody know how to move the keyboard caret in a textbox to a particular position?

For example, if a text-box (e.g. input element, not text-area) has 50 characters in it and I want to position the caret before character 20, how would I go about it?

This is in differentiation from this question: jQuery Set Cursor Position in Text Area , which requires jQuery.

Community
  • 1
  • 1
jonhobbs
  • 26,684
  • 35
  • 115
  • 170

11 Answers11

247

Excerpted from Josh Stodola's Setting keyboard caret Position in a Textbox or TextArea with Javascript

A generic function that will allow you to insert the caret at any position of a textbox or textarea that you wish:

function setCaretPosition(elemId, caretPos) {
    var elem = document.getElementById(elemId);

    if(elem != null) {
        if(elem.createTextRange) {
            var range = elem.createTextRange();
            range.move('character', caretPos);
            range.select();
        }
        else {
            if(elem.selectionStart) {
                elem.focus();
                elem.setSelectionRange(caretPos, caretPos);
            }
            else
                elem.focus();
        }
    }
}

The first expected parameter is the ID of the element you wish to insert the keyboard caret on. If the element is unable to be found, nothing will happen (obviously). The second parameter is the caret positon index. Zero will put the keyboard caret at the beginning. If you pass a number larger than the number of characters in the elements value, it will put the keyboard caret at the end.

Tested on IE6 and up, Firefox 2, Opera 8, Netscape 9, SeaMonkey, and Safari. Unfortunately on Safari it does not work in combination with the onfocus event).

An example of using the above function to force the keyboard caret to jump to the end of all textareas on the page when they receive focus:

function addLoadEvent(func) {
    if(typeof window.onload != 'function') {
        window.onload = func;
    }
    else {
        if(func) {
            var oldLoad = window.onload;

            window.onload = function() {
                if(oldLoad)
                        oldLoad();

                func();
            }
        }
    }
}

// The setCaretPosition function belongs right here!

function setTextAreasOnFocus() {
/***
 * This function will force the keyboard caret to be positioned
 * at the end of all textareas when they receive focus.
 */
    var textAreas = document.getElementsByTagName('textarea');

    for(var i = 0; i < textAreas.length; i++) {
        textAreas[i].onfocus = function() {
            setCaretPosition(this.id, this.value.length);
        }
    }

    textAreas = null;
}

addLoadEvent(setTextAreasOnFocus);
Cave Johnson
  • 6,499
  • 5
  • 38
  • 57
Ta01
  • 31,040
  • 13
  • 70
  • 99
  • 6
    `if(elem.selectionStart)` breaks when selectionStart is 0 as also pointed out by jhnns's answer. – mpartel Sep 01 '12 at 12:51
  • 1
    This doesn't work for me. When I click the actual textbox I have I expect the caret position to be in the beginning. Check out my fiddle http://jsfiddle.net/khx2w/ – elad.chen Jul 18 '13 at 20:53
  • The setCaretPosition function works just fine. Your addActionHandler function, however, does not. But even without that, I could not get the cursor to move on focus. Most likely, it's because the browser sets the cursor position itself based on the position of the click. There doesn't seem to be a way around it from what I can tell, but I could be completely wrong. – GJK Jul 19 '13 at 12:30
  • Works in IE9, FF25, but not Chrome 30. Better than none! – Umber Ferrule Nov 14 '13 at 13:17
  • I have `elem.value = elem.value.replace(/^9/g,'+79')` in code. On OperaMINI cursor after `+`. this function not helps – eri Dec 26 '16 at 18:00
  • 1
    The blog link is dead. – Cloud Jan 31 '20 at 14:53
  • @Cloud that’s why an excerpt is above. – Ta01 Jan 31 '20 at 14:54
  • Whoops, I misunderstood "excerpted". Too used to seeing small blockquotes in yellow. I see, thanks for the instant response. – Cloud Jan 31 '20 at 14:58
  • Just putting it here that this here appears to use an [MS-specific function](https://stackoverflow.com/a/41743191/11536796). I am still looking into other solutions. – Cloud Jan 31 '20 at 15:18
  • Note that as of 2020, this answer has become a bit misleading because it contains lots of code to deal with browsers that don't support setSelectionRange: that made sense in 2009, but hasn't been the case since 2016, when the last browser that didn't support it --IE8-- was finally killed off. Since then, there has been no need for fallback code. Also note that the whole "onload" procedure is now covered by the `defer` attribute on the ` – Mike 'Pomax' Kamermans Oct 16 '20 at 16:29
54

The link in the answer is broken, this one should work (all credits go to blog.vishalon.net):

http://snipplr.com/view/5144/getset-cursor-in-html-textarea/

In case the code gets lost again, here are the two main functions:

function doGetCaretPosition(ctrl)
{
 var CaretPos = 0;

 if (ctrl.selectionStart || ctrl.selectionStart == 0)
 {// Standard.
  CaretPos = ctrl.selectionStart;
 }
 else if (document.selection)
 {// Legacy IE
  ctrl.focus ();
  var Sel = document.selection.createRange ();
  Sel.moveStart ('character', -ctrl.value.length);
  CaretPos = Sel.text.length;
 }

 return (CaretPos);
}


function setCaretPosition(ctrl,pos)
{
 if (ctrl.setSelectionRange)
 {
  ctrl.focus();
  ctrl.setSelectionRange(pos,pos);
 }
 else if (ctrl.createTextRange)
 {
  var range = ctrl.createTextRange();
  range.collapse(true);
  range.moveEnd('character', pos);
  range.moveStart('character', pos);
  range.select();
 }
}
h7r
  • 4,944
  • 2
  • 28
  • 31
eternal
  • 557
  • 4
  • 3
  • 1
    +1 for the base functions, though **some adjustments have to be made**. The number of lines has to be substracted from "pos", when using the range method. – Rob W Jun 05 '12 at 13:33
40

Since I actually really needed this solution, and the typical baseline solution (focus the input - then set the value equal to itself) doesn't work cross-browser, I spent some time tweaking and editing everything to get it working. Building upon @kd7's code here's what I've come up with.

Enjoy! Works in IE6+, Firefox, Chrome, Safari, Opera

Cross-browser caret positioning technique (example: moving the cursor to the END)

// ** USEAGE ** (returns a boolean true/false if it worked or not)
// Parameters ( Id_of_element, caretPosition_you_want)

setCaretPosition('IDHERE', 10); // example

The meat and potatoes is basically @kd7's setCaretPosition, with the biggest tweak being if (el.selectionStart || el.selectionStart === 0), in firefox the selectionStart is starting at 0, which in boolean of course is turning to False, so it was breaking there.

In chrome the biggest issue was that just giving it .focus() wasn't enough (it kept selecting ALL of the text!) Hence, we set the value of itself, to itself el.value = el.value; before calling our function, and now it has a grasp & position with the input to use selectionStart.

function setCaretPosition(elemId, caretPos) {
    var el = document.getElementById(elemId);

    el.value = el.value;
    // ^ this is used to not only get "focus", but
    // to make sure we don't have it everything -selected-
    // (it causes an issue in chrome, and having it doesn't hurt any other browser)

    if (el !== null) {

        if (el.createTextRange) {
            var range = el.createTextRange();
            range.move('character', caretPos);
            range.select();
            return true;
        }

        else {
            // (el.selectionStart === 0 added for Firefox bug)
            if (el.selectionStart || el.selectionStart === 0) {
                el.focus();
                el.setSelectionRange(caretPos, caretPos);
                return true;
            }

            else  { // fail city, fortunately this never happens (as far as I've tested) :)
                el.focus();
                return false;
            }
        }
    }
}
Mark Pieszak - Trilon.io
  • 61,391
  • 14
  • 82
  • 96
  • @mcpDESIGNS I'm trying to use this function when a focus is made on a text input, but I'm getting nowhere, Could you please help with applying this function using pure javascript? – elad.chen Jul 19 '13 at 11:30
  • @elad.chen Are you trying to position them to the end of the textbox as soon as they focus on it? – Mark Pieszak - Trilon.io Jul 19 '13 at 14:11
  • @mcpDESIGNS I want to place the cursor in the beginning of the field as soon as the element is being focused. See the fiddle: jsfiddle.net/KuLTU/3 – elad.chen Jul 19 '13 at 17:16
  • @elad.chen Sorry for the crazy late response! Just change that event to `.onclick` instead of `.onfocus` within your search_field_focus function – Mark Pieszak - Trilon.io Jan 23 '15 at 15:06
  • 2
    `el.value = el.value; [...] if(el !== null)` - I would swap these two lines. If `el` is null, `el.value = el.value` will fail, so that line should be inside the `if`. – BackSlash May 07 '18 at 09:01
24

I found an easy way to fix this issue, tested in IE and Chrome:

function setCaret(elemId, caret)
 {
   var elem = document.getElementById(elemId);
   elem.setSelectionRange(caret, caret);
 }

Pass text box id and caret position to this function.

Striped
  • 2,544
  • 3
  • 25
  • 31
Iam_NSA
  • 404
  • 1
  • 5
  • 12
  • 1
    Short and elegant – Yaniv K. Sep 21 '21 at 12:08
  • Yes, using `setSelectionRange` was the solution I was searching for. The position is zero based, and setting it to a value grater than the length moves the caret to the last position. I get the current length with `const len = elem.value.length` and move the caret to the last position by `elem.setSelectionRange(len,len)`; – some Jun 05 '23 at 20:53
22

I've adjusted the answer of kd7 a little bit because elem.selectionStart will evaluate to false when the selectionStart is incidentally 0.

function setCaretPosition(elem, caretPos) {
    var range;

    if (elem.createTextRange) {
        range = elem.createTextRange();
        range.move('character', caretPos);
        range.select();
    } else {
        elem.focus();
        if (elem.selectionStart !== undefined) {
            elem.setSelectionRange(caretPos, caretPos);
        }
    }
}
Johannes Ewald
  • 17,665
  • 5
  • 44
  • 39
3

If you need to focus some textbox and your only problem is that the entire text gets highlighted whereas you want the caret to be at the end, then in that specific case, you can use this trick of setting the textbox value to itself after focus:

$("#myinputfield").focus().val($("#myinputfield").val());
John
  • 1
  • 13
  • 98
  • 177
Manish Singh
  • 5,848
  • 4
  • 43
  • 31
2

I would fix the conditions like below:

function setCaretPosition(elemId, caretPos)
{
 var elem = document.getElementById(elemId);
 if (elem)
 {
  if (typeof elem.createTextRange != 'undefined')
  {
   var range = elem.createTextRange();
   range.move('character', caretPos);
   range.select();
  }
  else
  {
   if (typeof elem.selectionStart != 'undefined')
    elem.selectionStart = caretPos;
   elem.focus();
  }
 }
}
2
function SetCaretEnd(tID) {
    tID += "";
    if (!tID.startsWith("#")) { tID = "#" + tID; }
    $(tID).focus();
    var t = $(tID).val();
    if (t.length == 0) { return; }
    $(tID).val("");
    $(tID).val(t);
    $(tID).scrollTop($(tID)[0].scrollHeight); }
John
  • 1
  • 13
  • 98
  • 177
  • Uncaught TypeError: Object [object HTMLInputElement] has no method 'startsWith' – bobpaul Sep 02 '11 at 20:08
  • 1
    @bobpaul: `tID` should be the id of the element, not the element object itself. If you are passing the element object, you can just remove the 2 first lines in this method, and it will work. – awe Nov 25 '11 at 09:15
2

HTMLInputElement.setSelectionRange( selectionStart, selectionEnd );

// References
var e = document.getElementById( "helloworldinput" );

// Move caret to beginning on focus
e.addEventListener( "focus", function( event )
{
    // References
    var e = event.target;

    // Action
    e.setSelectionRange( 0, 0 );            // Doesn’t work for focus event
    
    window.setTimeout( function()
    {
        e.setSelectionRange( 0, 0 );        // Works
        //e.setSelectionRange( 1, 1 );      // Move caret to second position
        //e.setSelectionRange( 1, 2 );      // Select second character

    }, 0 );

}, false );

Browser compatibility (only for types: text, search, url, tel and password): https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange#Specifications

Starboy
  • 109
  • 5
1
<!DOCTYPE html>
<html>
<head>
<title>set caret position</title>
<script type="application/javascript">
//<![CDATA[
window.onload = function ()
{
 setCaret(document.getElementById('input1'), 13, 13)
}

function setCaret(el, st, end)
{
 if (el.setSelectionRange)
 {
  el.focus();
  el.setSelectionRange(st, end);
 }
 else
 {
  if (el.createTextRange)
  {
   range = el.createTextRange();
   range.collapse(true);
   range.moveEnd('character', end);
   range.moveStart('character', st);
   range.select();
  }
 }
}
//]]>
</script>
</head>
<body>

<textarea id="input1" name="input1" rows="10" cols="30">Happy kittens dancing</textarea>

<p>&nbsp;</p>

</body>
</html>
John
  • 1
  • 13
  • 98
  • 177
DIpak
  • 29
  • 1
0

Here's a variation of the accepted answer:

  • allows to specify the target element by passing any valid CSS selector OR by providing the element itself
  • written in TypeScript
  • class-based
  • SRP implemented by splitting the solution into a number of smaller functions
  • throws custom errors
// CaretPositioner.ts

class ElementNotFoundError extends Error {}
class InvalidArgumentValueError extends Error {}

type HTMLInputOrTextAreaElement = HTMLInputElement | HTMLTextAreaElement

class CaretPositioner {
  public static setCaretPosition (
    element: HTMLInputOrTextAreaElement,
    caretPosition: number,
  ): void

  public static setCaretPosition (
    selector: string,
    caretPosition: number,
  ): void

  public static setCaretPosition (
    elementOrSelector: HTMLInputOrTextAreaElement | string,
    caretPosition: number,
  ): void {
    const element: HTMLInputOrTextAreaElement =
      CaretPositioner.determineElement(elementOrSelector)

    CaretPositioner._setCaretPosition(element, caretPosition)
  }

  private static determineElement (
    elementOrSelector: HTMLInputOrTextAreaElement | string,
  ): HTMLInputOrTextAreaElement {
    if (
      (elementOrSelector instanceof HTMLInputElement) ||
      (elementOrSelector instanceof HTMLTextAreaElement)
    ) {
      return elementOrSelector
    } else if (CaretPositioner.isValidCssSelector(elementOrSelector)) {
      return CaretPositioner.getInputElementBySelector(elementOrSelector)
    } else {
      throw new InvalidArgumentValueError()
    }
  }

  private static isValidCssSelector (subject: unknown): boolean {
    return typeof subject === 'string' &&
      subject.length > 0
  }

  private static getInputElementBySelector (
    selector: string,
  ): HTMLInputOrTextAreaElement {
    const element = document.querySelector(selector)

    if (
      !(element instanceof HTMLInputElement) &&
      !(element instanceof HTMLTextAreaElement)
    ) {
      throw new ElementNotFoundError('element')
    }

    return element
  }

  private static _setCaretPosition (
    element: HTMLInputOrTextAreaElement,
    caretPosition: number,
  ): void {
    element.focus()
    element.setSelectionRange(caretPosition, caretPosition)
  }
}

I wanted to add this piece as a comment to the aforementioned answer, but I have not enough reputation points to do so.