Set keyboard caret position in html textbox
Asked Answered
L

11

236

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.

Lucky answered 4/2, 2009 at 17:54 Comment(0)
F
250

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);
Fabriane answered 4/2, 2009 at 17:59 Comment(10)
if(elem.selectionStart) breaks when selectionStart is 0 as also pointed out by jhnns's answer.Monandry
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 jsfiddle.net/khx2wTilly
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.Fiddlededee
Works in IE9, FF25, but not Chrome 30. Better than none!Assize
I have elem.value = elem.value.replace(/^9/g,'+79') in code. On OperaMINI cursor after +. this function not helpsSeaborne
The blog link is dead.Kronfeld
@Kronfeld that’s why an excerpt is above.Fabriane
Whoops, I misunderstood "excerpted". Too used to seeing small blockquotes in yellow. I see, thanks for the instant response.Kronfeld
Just putting it here that this here appears to use an MS-specific function. I am still looking into other solutions.Kronfeld
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 <script> element (making the script run only after document is fully parsed), which has enjoyed universal support since IE10 (which has itself gone EOL since)Anguilla
F
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();
 }
}
Felly answered 29/11, 2010 at 10:11 Comment(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.Clue
F
41

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;
            }
        }
    }
}
Footed answered 20/9, 2012 at 18:32 Comment(5)
@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?Tilly
@Tilly Are you trying to position them to the end of the textbox as soon as they focus on it?Footed
@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/3Tilly
@Tilly Sorry for the crazy late response! Just change that event to .onclick instead of .onfocus within your search_field_focus functionFooted
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.Abed
C
25

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.

Cowpea answered 29/8, 2019 at 12:13 Comment(2)
Short and elegantQueri
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);Fluidics
D
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);
        }
    }
}
Dichroscope answered 19/4, 2012 at 11:54 Comment(0)
S
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());
Smirk answered 14/1, 2014 at 8:47 Comment(0)
H
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); }
Hollands answered 23/3, 2011 at 16:44 Comment(2)
Uncaught TypeError: Object [object HTMLInputElement] has no method 'startsWith'Apanage
@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.Philosophical
A
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();
  }
 }
}
Alert answered 31/5, 2018 at 4:55 Comment(0)
C
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

Coppersmith answered 20/12, 2020 at 16:22 Comment(0)
D
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>
Darell answered 14/8, 2011 at 14:4 Comment(1)
This does nothingExophthalmos
A
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.

Arak answered 2/3, 2023 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.