Home > Javascript > Javascript : Getting and Setting Caret Position in Textarea

Javascript : Getting and Setting Caret Position in Textarea

December 27th, 2005 Leave a comment Go to comments
function doGetCaretPosition (ctrl) {
	var CaretPos = 0;	// IE Support
	if (document.selection) {
	ctrl.focus ();
		var Sel = document.selection.createRange ();
		Sel.moveStart ('character', -ctrl.value.length);
		CaretPos = Sel.text.length;
	}
	// Firefox support
	else if (ctrl.selectionStart || ctrl.selectionStart == '0')
		CaretPos = ctrl.selectionStart;
	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();
	}
}

Here “ctrl” is the Textarea object. This script works well with Internet Explorer, Firefox and Opera.

You can check it at Get/Set Caret in Textarea Example

Categories: Javascript Tags:
  1. adnrey
    January 18th, 2006 at 04:50 | #1

    doesnt work with interenet explorer!

  2. January 22nd, 2006 at 01:33 | #2

    Thanks adnrey,

    My bad. I posted few lines wrong, but now i checked it and it is working with IE.

  3. Chris
    February 19th, 2006 at 07:43 | #3

    Nice simple information I have been looking for. It seems there are many (mostly too complex) ways of doing this in IE.

    However, doGetCaretPosition does not work in IE if there are carriage returns in the textarea. I’m not sure exactly why but IE must remove them from the selection or something. The returned caret position will be off by one for every carriage return in the textarea prior to the caret position.

    Any ideas on how to fix this?

  4. Chris
    February 19th, 2006 at 07:49 | #4

    Er, actually I meant line-feeds, not carriage returns.

  5. Chris
    February 19th, 2006 at 02:57 | #5

    After playing with this more I found that it does not work in IE. If you have more than one control, or text on the page it simply will not work.

    The problem is Sel.moveStart (‘character’, -ctrl.value.length); which will move the selection to incorporate data not in the current control if there is any. So the length comes out all wrong except for the first item on the page.

  6. April 5th, 2006 at 07:40 | #6

    One of the few javascript examples that actually worked the way I wanted it to. Thanks!

  7. June 25th, 2006 at 03:14 | #7

    COOOOOL THE ONE THAT EVER WORKED FOR ME LOTS OF THAAANKS

  8. Thay
    August 2nd, 2006 at 10:57 | #8

    Thanks for the code.

  9. Peder
    September 14th, 2006 at 09:09 | #9

    The code above didn’t work for me on IE. The following seems to fix the problem. Thanks for helping me get in the right direction.

    function getCaretPosition (el)
    {
    var iCaretPos = 0

    if (document.selection) // IE hack
    {
    if (el.type == ‘text’) // textbox
    {
    var selectionRange = document.selection.createRange()
    selectionRange.moveStart (‘word’, -el.value.length)
    iCaretPos = selectionRange.text.length
    }
    else // textarea
    {
    iCaretPos = Math.abs(document.selection.createRange().moveStart(“character”, -1000000)) – 193;
    }
    }
    else if (el.selectionStart || el.selectionStart == ‘0′) // Firefox
    {
    iCaretPos = el.selectionStart
    }

    return iCaretPos;
    }

    function setCaretPosition (el, iCaretPos)
    {
    if (document.

  10. Peder
    September 14th, 2006 at 09:11 | #10

    Seems like the setCaretPosition function got truncated in my prior posting:

    function setCaretPosition (el, iCaretPos)
    {
    if (document.selection) // IE
    {
    var range

    range = document.selection.createRange()

    if (el.type == ‘text’) // textbox
    {
    range.moveStart (‘character’, -el.value.length)
    range.moveEnd (‘character’, -el.value.length)
    range.moveStart (‘character’, iCaretPos)
    range.select()
    }
    else // textarea
    {
    range.collapse(false)
    range.move (‘character’, iCaretPos – el.value.length + el.value.split(‘\n’).length – 1)
    range.select()
    }
    }
    else if (el.selectionStart || el.selectionStart == ‘0′) // Firefox
    {
    el.setSelectionRange(iCaretPos, iCaretPos)
    }

  11. drew
    November 1st, 2006 at 03:31 | #11

    Hi Peder – this was helpful but I did find it only worked correctly with the caret in the last line.

    By changing the split part to….

    el.value.substring(iCaretPos).split(‘\n’).length

    …it worked perfectly, since I was then only counting line breaks between the caret and the end rather than all of them.

  12. Ric
    February 15th, 2007 at 04:16 | #12

    It’s a shame you didn’t credit the original authors for what is obviously code ripped off from PHPMyAdmin!!!

  13. Vishal Monpara
    February 19th, 2007 at 08:20 | #13

    Ric,

    When I wanted this functionality, I tried google and got code from some website (surely not PHPMyAdmin website). I tried that code and did not workout. Then from that code, using trial and error method (I am NOT javascript programmer), I got this code. I dont know if it is “ripped off” version of PHPMyAdmin or not.

  14. rajesh
    June 14th, 2007 at 01:16 | #14

    i am developing a web page in which i need to enter a text into a textbox at the cursor position, on button click from another text box.

    Please help me if know how it is to be done

  15. August 13th, 2007 at 07:29 | #15

    I have this:
    function getCursorPos(textElement){
    var cursorPos = -1;
    if (textElement && textElement.createTextRange) {
    var range = document.selection.createRange().duplicate();
    range.setEndPoint(‘StartToEnd’,range);
    var start = document.body.createTextRange();
    start.moveToElementText(textElement);
    cursorPos = calcBookmark(range.getBookmark())-calcBookmark(start.getBookmark());
    var rLen = 0;
    do{
    var BrLen = rLen;
    rLen = newLine(textElement.value.substring(0,cursorPos + rLen + 1));
    }while(BrLen!=rLen);
    cursorPos += rLen;
    }
    return cursorPos;
    }
    function calcBookmark(bk){
    return (bk.charCodeAt(0)-1)+(bk.charCodeAt(3)-1)*65536+(bk.charCodeAt(2)-1);
    }
    It work’s for textarea in IE

  16. August 13th, 2007 at 07:41 | #16

    ups. I missed this:<SCRIPT LANGUAGE=VBSCRIPT>
    option explicit
    function newLine(str)
    dim nl, r
    set nl = new RegExp
    nl.global = true
    nl.pattern = “\r\n”
    set r = nl.Execute(str)
    newLine = r.count
    set r = nothing
    set nl = nothing
    end function
    </SCRIPT>

  17. Alex
    August 18th, 2007 at 02:01 | #17

    Thanx a lot, that’s what I needed – it works nice with Opera 8.5 as well

  18. Alex
    August 19th, 2007 at 03:35 | #18

    OK, I get it – it does not work in my [...]ing MSIE.

    Here is slightly modified code compatible to IE 6.0 and Opera:

    function GetCaretPosition(control)
    {
    var CaretPos = 0;
    // IE Support
    if (document.selection)
    {
    control.focus();
    var Sel = document.selection.createRange ();
    var Sel2 = Sel.duplicate();
    Sel2.moveToElementText(control);
    var CaretPos = -1;
    while(Sel2.inRange(Sel))
    {
    Sel2.moveStart(‘character’);
    CaretPos++;
    }
    }

    // Firefox support

    else if (control.selectionStart || control.selectionStart == ‘0′)
    CaretPos = control.selectionStart;

    return (CaretPos);

    }

  19. October 2nd, 2007 at 02:48 | #19

    This is just great! I’d been trying out all sorts of things which were half-working. This is simple and works well across browsers.

  20. John S
    October 10th, 2007 at 01:37 | #20

    just wanted to say thanks, this helped me a lot

  21. dumbo
    January 28th, 2008 at 05:38 | #21

    Alex you are awesome! Only your code worked for me!

    BTW, damn you IE!!!

  22. January 28th, 2008 at 05:39 | #22

    thank you!

  23. February 6th, 2008 at 06:15 | #23

    I thought I’d let you know that your code was useful to me in a way that you probably hadn’t expected. =)

    http://my.opera.com/community/forums/topic.dml?id=222466

  24. ramesh
    February 13th, 2008 at 02:18 | #24

    It is not working fine for ContentEditable Div
    How it can be

  25. ledaker
    April 10th, 2008 at 10:00 | #25

    Its not working with firefox

  26. zab
    May 30th, 2008 at 07:52 | #26

    with Firefox, I always end up inserting text about 30 chars before the actual caret position! Any hint why?

  27. August 18th, 2008 at 02:25 | #27

    It works! Awesome man, thank you!

  28. AndyT
    November 9th, 2008 at 23:33 | #28

    The original script works only on textarea without carriage return. The position is off by one for each Enter hit. Is there a fix for this? I’m trying to implement an autofill script in the textarea and need to find the exact position.

  29. Pal
    January 18th, 2009 at 17:54 | #29

    Since this thread, and especially Alex version, helped me solve my problem, I thought I’d paste it here to solve other people’s problems.

    Alex version worked for FF, IE and Safari for Textareas and for FF and Safari for input text. But it didn’t work at all for IE input texts (threw “Invalid argument” at “moveToElementText(control)”).

    So, I fixed that problem by doing “expand(‘textedit’)” instead. Now it worked in all sex occasions, except when the cursor ended up dead last in the input text. It made IE freeze inside the “moving selection one step loop”. So, I added a maximum bail-out value at the length of the text. Then it finally worked for IE, FF and Safari in both text areas and input texts!

    I also added a global variable to keep track of the position when out of focus in IE (the other ones did that by themselves).

    Some variables have new names, and I have no idea what this forum will do to qoutes and stuff, but here goes:

    var cursorPosSaved = new Array();

    function getCursorPos(id) {
    control = document.getElementById(id);
    var cursorPos = (cursorPosSaved[control.id]) ? cursorPosSaved[control.id] : 0;
    if (document.selection) {
    control.focus();
    var oldRange = document.selection.createRange ();
    var newRange = oldRange.duplicate();
    newRange.expand(‘textedit’);
    var cursorPos = -1;
    while(newRange.inRange(oldRange)) {
    newRange.moveStart(‘character’);
    cursorPos++;
    if (cursorPos == control.value.length) {
    break;
    }
    }
    } else if (control.selectionStart || control.selectionStart == ‘0′) {
    cursorPos = control.selectionStart;
    }
    cursorPosSaved[control.id] = cursorPos;
    debug(cursorPos);
    return cursorPos;
    }

  30. Pal
    January 18th, 2009 at 18:31 | #30

    Okay, that didn’t work in Opera, since Opera supports both kind of objects, but it doesn’t support the expand method. Sigh.

    Anyway, it supports the FF/Safari stuff, so changing the order of the methods solved that problem.

    I also had to fix my onwhataver-stuff, since IE/FF/Safari fires onkeydown for the arrow keys (which change the cursor position), but Opera does not. Opera fired onkeypress but not onkeydown. So, I hade to build make the function detect itself and put in both onwhatevers… sigh again!

    Anyway, here’s the Opera comatible code:

    function getCursorPos(id) {
    control = document.getElementById(id);
    var cursorPos = (cursorPosSaved[control.id]) ? cursorPosSaved[control.id] : 0;
    if (window.getSelection) {
    cursorPos = control.selectionStart;
    } else if (document.selection) {
    control.focus();
    var oldRange = document.selection.createRange ();
    var newRange = oldRange.duplicate();
    newRange.expand(‘textedit’);
    var cursorPos = -1;
    while(newRange.inRange(oldRange)) {
    newRange.moveStart(‘character’);
    cursorPos++;
    if (cursorPos == control.value.length) {
    break;
    }
    }
    }
    cursorPosSaved[control.id] = cursorPos;
    return cursorPos;
    }

    And the onwhateverstuff:

    function registerTimeField(field) {
    field.onkeypress = function(e) {
    getCursorPos(field.id);
    var keyCode = (window.event) ? window.event.keyCode : e.which;
    keyChar = String.fromCharCode(keyCode);
    debug(‘o’+keyCode);
    if (keyCode == 8 || keyCode == 0 || keyCode == 37 || keyCode == 39 || (keyCode > 115 && keyCode < 124) || keyCode == 45 || keyCode == 46) {
    return true;
    } else if (keyCode == 38 || keyCode == 40) {
    (keyCode == 38) ? do arrow-up-stuff : do arrow-down-stuff ;
    } else {
    return (detect-validation-error-stuff) ? false : true;
    }
    }
    field.onkeydown = function(e) {
    getCursorPos(field.id);
    var keyCode = (window.event) ? window.event.keyCode : e.which;
    debug(keyCode);
    if (keyCode == 38 || keyCode == 40) {
    (keyCode == 38) ? do arrow-up-stuff : do arrow-down-stuff;
    (e) ? e.stopPropagation : window.event.cancelBubble = true;
    return false;
    } else {
    return true;
    }
    }
    }

    HTML:

  31. Pal
    January 18th, 2009 at 18:33 | #31

    Somone ate my HTML!! New try:

  32. Pal
    January 18th, 2009 at 18:34 | #32

    Okay, he’s hungry… new, smarter, try:

    <input name=”something” id=”something” type=”text” onclick=”getCursorPos(this.id)” onkeyup=”getCursorPos(this.id);” onfocus=”registerTimeField(this);” value=” : “/>

  33. bayu
    April 18th, 2009 at 10:56 | #33

    how can I paste some text in the current position assigned by those script? Thanks for your help..

  34. phuong
    June 2nd, 2009 at 04:17 | #34

    @Alex
    hi Alex
    i had apply your getcareposition function it work fine, but seem to be my problem come from setcareposition function i hand used setcareposition (18) after that i use getcareposition and the result is 20
    any idea from you, many thank for your help
    VP

  35. sara
    December 9th, 2009 at 06:44 | #35

    hellow please help me :this code dont work in firefox whyyyyyyyy?
    function number_textbox()
    {
    if (window.event.keycode57)
    {alert(“لطفاًعدد وارد نماييد”)
    window.event.cancelBubble = true
    window.event.returnValue = false;

    }
    thanks a lot!!!!!!!!!

  36. December 29th, 2009 at 01:28 | #36

    I Found out that IE counts caret position using only 1 character for Carriage Return / Line Feed even though the textarea.innerText contains 2 characters.

    So when Counting Caret Position either for setting or retrieving, you should count text by deleting either the CR or LF in a scratch work area:

    data = mytext.innerText.replace(/\r/g,”);
    pos = data.indexOf(‘Hello World’);

    This is only for IE, I hope this helps…
    Clif

  37. Glenn
    March 19th, 2010 at 08:04 | #37

    Has anybody tested this code when the text scrolls out of view? In playing with the example (http://demo.vishalon.net/getset.htm), with IE 7, the cursor will go to the desired spot AND will scroll to the appropriate view. With Firefox (3.6), the cursor goes to correct spot BUT the scroll view does not change. Obviously, this would generate some negative feedback from users where text is being appended and they are forced to manually scroll to the bottom each time. Does anybody know of a fix for this? Other than this, the code seems to work really great!

    Glenn

  38. Glenn
    March 19th, 2010 at 10:04 | #38

    I finally found/created a workable answer to the issue of not scrolling in Firefox. Specifically, I have a an AJAX updatepanel with dropdown list and an “insert” button. The user selects a value from the dropdown list, clicks the Insert button, and the value is appended to the value in the textbox. IE was easy, but FF was a nightmare. The solution was in combining many parts of multiple answers found on the web (i.s no one solution seemed to work with Ajax. I am not a “Javascript” guy, so there is probably room for lots of improvement – but it works for me. I am primarily concerned with FF and IE, but it could easily be modded for other browsers. Here is the code:

    // Added for Ajax/Updatepanel
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_pageLoaded(pageLoaded);
    function pageLoaded(sender, args) {
    var oTextbox = document.getElementById(”);
    var browserout = findBrowser();
    if (browserout === “IE”) {
    if (oTextbox.createTextRange) {
    var r = (oTextbox.createTextRange());
    r.moveStart(‘character’, (oTextbox.value.length));
    r.collapse();
    r.select();
    }
    }
    else if (browserout === “FF”) {
    // Append a space and then backspace to delete it.
    var elem = document.getElementById(“TextBox1″);
    elem.focus();
    elem.setSelectionRange(elem.value.length, elem.value.length);
    evt = document.createEvent(“KeyboardEvent”);
    evt.initKeyEvent(“keypress”, true, true, null, false, false, false, false, 0, 32);
    elem.dispatchEvent(evt);
    evt = document.createEvent(“KeyboardEvent”);
    evt.initKeyEvent(“keypress”, true, true, null, false, false, false, false, 8, 0);
    elem.dispatchEvent(evt);
    }
    else if (browserout === “Other”) {
    if (oTextbox.createTextRange) {
    var r = (oTextbox.createTextRange());
    r.moveStart(‘character’, (oTextbox.value.length));
    r.collapse();
    r.select();
    }
    }
    }
    function findBrowser() {
    var browser = {
    ‘name’: navigator.appName,
    ‘version’: navigator.appVersion,
    ‘userAgent’: navigator.userAgent,

    // Functions returning true or false
    ‘isFF’: function() { return !(this.userAgent.indexOf(“Firefox”) == -1); },
    ‘isIE’: function() { return !(this.userAgent.indexOf(“MSIE”) == -1); },
    ‘isIE6′: function() { return !(this.userAgent.indexOf(“MSIE 6″) == -1); },
    ‘isIE7′: function() { return !(this.userAgent.indexOf(“MSIE 7″) == -1); },
    ‘isOpera’: function() { return !(this.userAgent.indexOf(“Opera”) == -1); },
    ‘isSafari’: function() { return !(this.userAgent.indexOf(“Safari”) == -1); }
    }
    if (browser.isFF() == 1) { return(“FF”); }
    else if (browser.isIE() == 1) { return (“IE”); }
    else { return (“Other”); }
    }

    Special thanks to all of the folks that have posted the parts I used to aggregate this. Extra special thanks to Saul on Stackoverflow for the “Keypress” idea!!!

  39. miller
    April 16th, 2010 at 12:11 | #39

    i have used the following on a dropdown which is a field on a grid.
    if (ctrl.createTextRange) {
    var range = ctrl.createTextRange();
    range.collapse(true);
    range.moveEnd(‘character’, pos);
    range.moveStart(‘character’, pos);
    range.select();
    }
    it selects the data fine. but the the method the focus doesn’t move from the dropdown. so, when i select the row on the grid, the dropdown always gets the focus. how can I set the focus out of the dropdown once the above code is run.
    Thank you.

  40. April 24th, 2010 at 03:17 | #40

    Great solution, thx :-)

  41. May 12th, 2010 at 00:49 | #41

    kindly dont block the freakin content with the ads

  42. Haig
    May 26th, 2010 at 08:24 | #42

    Hey everyone. This conversation has really helped point me in the right direction. I was having major issues with IE document selection. I tried many solutions, and found Alex’s to be the best. However, the code is really inefficient and starts to show when you’re working with a good amount of text in a textarea. I therefore made a few enhancements to Alex’s code to prevent looping through every character from the beginning of the textarea everytime you need to get cursor/selection positions. I also added the code to return the endpoint as well as the startpoint for selection range. I tested this in on FF3.5/Ubuntu/Win, IE8/Win, Chrome 4.1/Win, Chrome 5/Ubuntu, Safari4/Win, Opera10.53/Win, I hope this helps someone out. Cheers!

    var textArea = $(‘myTextArea’);
    var getPos = function() {setTimeout(“debugOutput();”, 1);};
    var pos = 0;

    textArea.onkeydown = getPos;

    function debugOutput() {
    var sel = getCursorPosition(textArea, pos);
    pos = sel.start;
    $(‘lblStart’).innerHTML = sel.start;
    $(‘lblEnd’).innerHTML = sel.end;
    }

    function getCursorPosition(el, pos) {
    if (document.selection) { // IE…
    el.focus();
    var sel = document.selection.createRange();
    var sel2 = sel.duplicate();
    sel2.moveToElementText(el);

    if(sel.text.length > 1) {
    pos = pos – sel.text.length;
    if(pos < 0) {
    pos = 0;
    }
    }

    var caretPos = -1 + pos;
    sel2.moveStart('character', pos);

    while(sel2.inRange(sel)) {
    sel2.moveStart('character');
    caretPos++;
    }
    var selStr = sel.text.replace(/\r/g, "");
    return {start: caretPos, end: selStr.length + caretPos};
    }else if (el.selectionStart || el.selectionStart == 0) { //Most other browsers
    return {start: el.selectionStart, end: el.selectionEnd};
    }
    }

    function $(el) {
    return document.getElementById(el);
    }

  43. Simon Sanderson
    June 9th, 2010 at 21:44 | #43

    @Alex this posting works just fantastic Alex August 19th, 2007 at 03:35 #18. I wasted about 3 days trying to understand othermechanisms to get around problems (some of) associated with IE V6.
    This was the only example of code that behaved the same in both FF and IE for me :-) )
    Thanks to you Alex

  44. Lucas
    June 30th, 2010 at 06:23 | #44

    @Alex
    You are the best!

  1. July 8th, 2010 at 03:24 | #1