451

How do you set the cursor position in a text field using jQuery? I've got a text field with content, and I want the users cursor to be positioned at a certain offset when they focus on the field. The code should look kind of like this:

$('#input').focus(function() {
  $(this).setCursorPosition(4);
});

What would the implementation of that setCursorPosition function look like? If you had a text field with the content abcdefg, this call would result in the cursor being positioned as follows: abcd**|**efg.

Java has a similar function, setCaretPosition. Does a similar method exist for javascript?

Update: I modified CMS's code to work with jQuery as follows:

new function($) {
  $.fn.setCursorPosition = function(pos) {
    if (this.setSelectionRange) {
      this.setSelectionRange(pos, pos);
    } else if (this.createTextRange) {
      var range = this.createTextRange();
      range.collapse(true);
      if(pos < 0) {
        pos = $(this).val().length + pos;
      }
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  }
}(jQuery);
Soul_man
  • 575
  • 5
  • 15
jcnnghm
  • 7,426
  • 7
  • 32
  • 38
  • 78
    `$(this).get(0).setSelectionRange)`? You do know that's exactly the same as `this.setSelectionRange`, only slower and harder to read, right? jQuery is doing literally nothing for you here. – bobince Apr 26 '10 at 10:37
  • 2
    To add to @bobince comment, the function should iterate for each of the selected elements and return this. The correct code is in my answer. – HRJ Sep 06 '10 at 11:57
  • 21
    @bobince is actually not quite correct either. 'this' is not the DOM node, but the jQuery object. So, $(this).get(0).setSelectionRange is the same as this.get(0).setSelectionRange, not the same as this.setSelectionRange. – Prestaul Sep 07 '10 at 23:15
  • $(this)[0] is faster then $(this).get(0) – EminezArtus Oct 01 '14 at 03:53
  • Check out this tutorial for complete solution. http://webdesignpluscode.blogspot.com/2017/05/how-to-set-and-get-cursor-position-in.html – Waqas Ali May 28 '17 at 11:14

16 Answers16

302

Here's a jQuery solution:

$.fn.selectRange = function(start, end) {
    if(end === undefined) {
        end = start;
    }
    return this.each(function() {
        if('selectionStart' in this) {
            this.selectionStart = start;
            this.selectionEnd = end;
        } else if(this.setSelectionRange) {
            this.setSelectionRange(start, end);
        } else if(this.createTextRange) {
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    });
};

With this, you can do

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
Web_Designer
  • 72,308
  • 93
  • 206
  • 262
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 2
    @Jesse: Dunno how that happened, I usually use 4. Fixed. – mpen Mar 12 '11 at 18:49
  • I would modify this, to take one argument `if (!end) { end = start; } return this.each(...`, Thanks – guzart May 08 '13 at 20:35
  • 1
    @UberNeet: Updated based on your suggestion. – mpen May 08 '13 at 20:46
  • 1
    @Enve: I don't have a copy of IE 5.5 to test with, but that would likely be because [jQuery doesn't support IE 5.5](http://jquery.com/browser-support/). – mpen Aug 23 '13 at 23:37
  • 1
    @JaroslavZáruba: Yes. It is. But allows you to *not* have to write `selectRange($('.my_input')[0], 3, 5)` if you're already using jQuery. Further, it should work with more than one element, if you need that, for whatever reason. If you want pure native, please use CMS's solution. – mpen Jan 10 '14 at 16:39
  • 1
    @Mark I have nothing against native JS – Jaroslav Záruba Jan 28 '14 at 13:01
  • @Mark, I had to change this.setSelectionRange(...) to this.selectionStart = start; this.selectionEnd = end; so that it would work on new Firefox. – squadette Sep 09 '15 at 20:19
  • @squadette Thank you for pointing that out. I put in a fix based on your suggestion. – mpen Sep 09 '15 at 20:42
  • 2
    I needed to add `$('#elem').focus()` beforehand to get the blinking cursor to appear. – mareoraft Nov 23 '15 at 19:09
  • @mareoraft Thanks for that tip. I found it cleanest to chain them like so: $('#elem').selectRange( 5, 10 ).focus(); – kevinmicke Feb 17 '19 at 19:17
264

I have two functions:

function setSelectionRange(input, selectionStart, selectionEnd) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  }
  else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  }
}

function setCaretToPos (input, pos) {
  setSelectionRange(input, pos, pos);
}

Then you can use setCaretToPos like this:

setCaretToPos(document.getElementById("YOURINPUT"), 4);

Live example with both a textarea and an input, showing use from jQuery:

function setSelectionRange(input, selectionStart, selectionEnd) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  } else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  }
}

function setCaretToPos(input, pos) {
  setSelectionRange(input, pos, pos);
}

$("#set-textarea").click(function() {
  setCaretToPos($("#the-textarea")[0], 10)
});
$("#set-input").click(function() {
  setCaretToPos($("#the-input")[0], 10);
});
<textarea id="the-textarea" cols="40" rows="4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</textarea>
<br><input type="button" id="set-textarea" value="Set in textarea">
<br><input id="the-input" type="text" size="40" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit">
<br><input type="button" id="set-input" value="Set in input">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

As of 2016, tested and working on Chrome, Firefox, IE11, even IE8 (see that last here; Stack Snippets don't support IE8).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 3
    Why would collapse(true) be necessary since you are going to set the end and start selection offsets? – Alexis Wilke Dec 31 '13 at 09:32
  • @mareoraft: Works on `textarea` (and `input`) for me on Chrome, Firefox, IE8, and IE11. – T.J. Crowder Jan 03 '16 at 13:49
  • I don't seem to be able to get this to work with my script. I have a text area that is empty on page load, then filled by javascript as the application is used. I want the caret to be returned to 0 before each new write (a record of the usage). is it the dynamic data that's causing me problems? if so how could i get around that? – CodingInTheUK Jun 03 '16 at 14:58
  • What's the significance of the string literal 'character'? Does that specific string need to be used? – Jon Schneider Mar 31 '17 at 15:46
  • According to createTextRange should be avoided. https://developer.mozilla.org/en-US/docs/Web/API/TextRange – Justin Jul 28 '21 at 19:38
37

The solutions here are right except for the jQuery extension code.

The extension function should iterate over each selected element and return this to support chaining. Here is the a correct version:

$.fn.setCursorPosition = function(pos) {
  this.each(function(index, elem) {
    if (elem.setSelectionRange) {
      elem.setSelectionRange(pos, pos);
    } else if (elem.createTextRange) {
      var range = elem.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  });
  return this;
};
HRJ
  • 17,079
  • 11
  • 56
  • 80
  • 4
    The each function returns the jquery object. so you can actually do: `return this.each(function...)` and remove the standalone line. – jhummel Apr 27 '14 at 20:23
24

I found a solution that works for me:

$.fn.setCursorPosition = function(position){
    if(this.length == 0) return this;
    return $(this).setSelection(position, position);
}

$.fn.setSelection = function(selectionStart, selectionEnd) {
    if(this.length == 0) return this;
    var input = this[0];

    if (input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
    } else if (input.setSelectionRange) {
        input.focus();
        input.setSelectionRange(selectionStart, selectionEnd);
    }

    return this;
}

$.fn.focusEnd = function(){
    this.setCursorPosition(this.val().length);
            return this;
}

Now you can move the focus to the end of any element by calling:

$(element).focusEnd();

Or you specify the position.

$(element).setCursorPosition(3); // This will focus on the third character.
José Veríssimo
  • 221
  • 2
  • 11
AVProgrammer
  • 1,344
  • 2
  • 20
  • 40
  • 4
    For textarea elements, an improvement to focusEnd is to add `this.scrollTop(this[0].scrollHeight);`, to ensure that the textarea is scrolled to make the insertion point visible. – Drew May 31 '15 at 16:31
  • According to Mozilla I'd just straight up avoid using anything that uses `createTextRange` - https://developer.mozilla.org/en-US/docs/Web/API/TextRange – Justin Jul 28 '21 at 19:37
13

This worked for me on Safari 5 on Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
BobFromBris
  • 261
  • 4
  • 6
  • for me that was not working fine with a direct access, but this worked perfectly. $(myID).prop('selectionStart', position); $(myID).prop('selectionEnd', position); – gxmad Apr 18 '20 at 20:47
9

I do realize that this is a very old post, but I thought that I should offer perhaps a simpler solution to update it using only jQuery.

function getTextCursorPosition(ele) {   
    return ele.prop("selectionStart");
}

function setTextCursorPosition(ele,pos) {
    ele.prop("selectionStart", pos + 1);
    ele.prop("selectionEnd", pos + 1);
}

function insertNewLine(text,cursorPos) {
    var firstSlice = text.slice(0,cursorPos);
    var secondSlice = text.slice(cursorPos);

    var new_text = [firstSlice,"\n",secondSlice].join('');

    return new_text;
}

Usage for using ctrl-enter to add a new line (like in Facebook):

$('textarea').on('keypress',function(e){
    if (e.keyCode == 13 && !e.ctrlKey) {
        e.preventDefault();
        //do something special here with just pressing Enter
    }else if (e.ctrlKey){
        //If the ctrl key was pressed with the Enter key,
        //then enter a new line break into the text
        var cursorPos = getTextCursorPosition($(this));                

        $(this).val(insertNewLine($(this).val(), cursorPos));
        setTextCursorPosition($(this), cursorPos);
    }
});

I am open to critique. Thank you.

UPDATE: This solution does not allow normal copy and paste functionality to work (i.e. ctrl-c, ctrl-v), so I will have to edit this in the future to make sure that part works again. If you have an idea how to do that, please comment here, and I will be happy to test it out. Thanks.

tofirius
  • 173
  • 2
  • 7
7

Set the focus before you have inserted the text into the textarea thus?

$("#comments").focus();
$("#comments").val(comments);
Steven Whitby
  • 87
  • 1
  • 1
7

I'm using this: http://plugins.jquery.com/project/jCaret

Ben Noland
  • 34,230
  • 18
  • 50
  • 51
7

In IE to move cursor on some position this code is enough:

var range = elt.createTextRange();
range.move('character', pos);
range.select();
6

This works for me in chrome

$('#input').focus(function() {
    setTimeout( function() {
        document.getElementById('input').selectionStart = 4;
        document.getElementById('input').selectionEnd = 4;
    }, 1);
});

Apparently you need a delay of a microsecond or more, because usually a user focusses on the text field by clicking at some position in the text field (or by hitting tab) which you want to override, so you have to wait till the position is set by the user click and then change it.

Hung Tran
  • 133
  • 1
  • 6
4

Small modification to the code I found in bitbucket

Code is now able to select/highlight with start/end points if given 2 positions. Tested and works fine in FF/Chrome/IE9/Opera.

$('#field').caret(1, 9);

The code is listed below, only a few lines changed:

(function($) {
  $.fn.caret = function(pos) {
    var target = this[0];
    if (arguments.length == 0) { //get
      if (target.selectionStart) { //DOM
        var pos = target.selectionStart;
        return pos > 0 ? pos : 0;
      }
      else if (target.createTextRange) { //IE
        target.focus();
        var range = document.selection.createRange();
        if (range == null)
            return '0';
        var re = target.createTextRange();
        var rc = re.duplicate();
        re.moveToBookmark(range.getBookmark());
        rc.setEndPoint('EndToStart', re);
        return rc.text.length;
      }
      else return 0;
    }

    //set
    var pos_start = pos;
    var pos_end = pos;

    if (arguments.length > 1) {
        pos_end = arguments[1];
    }

    if (target.setSelectionRange) //DOM
      target.setSelectionRange(pos_start, pos_end);
    else if (target.createTextRange) { //IE
      var range = target.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos_end);
      range.moveStart('character', pos_start);
      range.select();
    }
  }
})(jQuery)
twig
  • 4,034
  • 5
  • 37
  • 47
4

Just remember to return false right after the function call if you're using the arrow keys since Chrome fricks the frack up otherwise.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
erich
  • 41
  • 1
  • 2
    It’s not best practice to `return false;`. You want `event.preventDefault();` instead. If you return false, you are implying `event.stopPropagation()` which is not always desirable – Alan H. May 08 '12 at 19:12
4

Based on this question, the answer will not work perfectly for ie and opera when there is new line in the textarea. The answer explain how to adjust the selectionStart, selectionEnd before calling setSelectionRange.

I have try the adjustOffset from the other question with the solution proposed by @AVProgrammer and it work.

function adjustOffset(el, offset) {
    /* From https://stackoverflow.com/a/8928945/611741 */
    var val = el.value, newOffset = offset;
    if (val.indexOf("\r\n") > -1) {
        var matches = val.replace(/\r\n/g, "\n").slice(0, offset).match(/\n/g);
        newOffset += matches ? matches.length : 0;
    }
    return newOffset;
}

$.fn.setCursorPosition = function(position){
    /* From https://stackoverflow.com/a/7180862/611741 */
    if(this.lengh == 0) return this;
    return $(this).setSelection(position, position);
}

$.fn.setSelection = function(selectionStart, selectionEnd) {
    /* From https://stackoverflow.com/a/7180862/611741 
       modified to fit https://stackoverflow.com/a/8928945/611741 */
    if(this.lengh == 0) return this;
    input = this[0];

    if (input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
    } else if (input.setSelectionRange) {
        input.focus();
        selectionStart = adjustOffset(input, selectionStart);
        selectionEnd = adjustOffset(input, selectionEnd);
        input.setSelectionRange(selectionStart, selectionEnd);
    }

    return this;
}

$.fn.focusEnd = function(){
    /* From https://stackoverflow.com/a/7180862/611741 */
    this.setCursorPosition(this.val().length);
}
Community
  • 1
  • 1
3

I had to get this working for contenteditable elements and jQuery and tought someone might want it ready to use:

$.fn.getCaret = function(n) {
    var d = $(this)[0];
    var s, r;
    r = document.createRange();
    r.selectNodeContents(d);
    s = window.getSelection();
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return s.anchorOffset;
};

$.fn.setCaret = function(n) {
    var d = $(this)[0];
    d.focus();
    var r = document.createRange();
    var s = window.getSelection();
    r.setStart(d.childNodes[0], n);
    r.collapse(true);
    s.removeAllRanges();
    s.addRange(r);
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return this;
};

Usage $(selector).getCaret() returns the number offset and $(selector).setCaret(num) establishes the offeset and sets focus on element.

Also a small tip, if you run $(selector).setCaret(num) from console it will return the console.log but you won't visualize the focus since it is established at the console window.

Bests ;D

FGZ
  • 31
  • 2
1

You can directly change the prototype if setSelectionRange does not exist.

(function() {
    if (!HTMLInputElement.prototype.setSelectionRange) {
        HTMLInputElement.prototype.setSelectionRange = function(start, end) {
            if (this.createTextRange) {
                var range = this.createTextRange();
                this.collapse(true);
                this.moveEnd('character', end);
                this.moveStart('character', start);
                this.select();
            }
        }
    }
})();
document.getElementById("input_tag").setSelectionRange(6, 7);

jsFiddle link

Anoop
  • 23,044
  • 10
  • 62
  • 76
0

As for me here the most easily way to add text(Tab -> \t) in textarea by cursor position and save the focus on cursor:

$('#text').keyup(function () {
    var cursor = $('#text').prop('selectionStart');
    //if cursot is first in textarea
    if (cursor == 0) {
        //i will add tab in line
        $('#text').val('\t' + $('#text').val());
        //here we set the cursor position
        $('#text').prop('selectionEnd', 1);
    } else {
        var value = $('#text').val();
        //save the value before cursor current position
        var valToCur = value.substring(0, cursor);
        //save the value after cursor current position
        var valAfter = value.substring(cursor, value.length);
        //save the new value with added tab in text
        $('#text').val(valToCur + '\t' + valAfter);
        //set focus of cursot after insert text (1 = because I add only one symbol)
        $('#text').prop('selectionEnd', cursor + 1);
    }
});
David Buck
  • 3,752
  • 35
  • 31
  • 35