178

How do you get the caret position in a <textarea> using JavaScript?

For example: This is| a text

This should return 7.

How would you get it to return the strings surrounding the cursor / selection?

E.g.: 'This is', '', ' a text'.

If the word “is” is highlighted, then it would return 'This ', 'is', ' a text'.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Restless Wanderer
  • 1,873
  • 3
  • 14
  • 7
  • See this question: http://stackoverflow.com/questions/164147/character-offset-in-an-internet-explorer-textrange and if you will have newlines, also the note about that here: http://stackoverflow.com/questions/235411/is-there-an-internet-explorer-approved-substitute-for-selectionstart-and-select#235582 – bobince Nov 04 '08 at 22:52
  • 1
    If you are using jQuery you can use jquery caret plugin $('textarea').getSelection().start http://plugins.jquery.com/plugin-tags/caret @++ – redochka Feb 07 '11 at 12:39
  • Found a good solution at http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/ I tested it in firefox and chrome, and it worked in both. The writer says it works in IE+Opera as well. – pycoder112358 Jan 15 '12 at 16:34
  • Simple use `textarea.selectionStart`, `textarea. selectionEnd`, `textarea.setSelectionRange` https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement – EAndreyF Nov 06 '15 at 16:03

4 Answers4

173

With Firefox, Safari (and other Gecko based browsers) you can easily use textarea.selectionStart, but for IE that doesn't work, so you will have to do something like this:

function getCaret(node) {
  if (node.selectionStart) {
    return node.selectionStart;
  } else if (!document.selection) {
    return 0;
  }

  var c = "\001",
      sel = document.selection.createRange(),
      dul = sel.duplicate(),
      len = 0;

  dul.moveToElementText(node);
  sel.text = c;
  len = dul.text.indexOf(c);
  sel.moveStart('character',-1);
  sel.text = "";
  return len;
}

(complete code here)

I also recommend you to check the jQuery FieldSelection Plugin, it allows you to do that and much more...

Edit: I actually re-implemented the above code:

function getCaret(el) { 
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
        rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    return rc.text.length; 
  }  
  return 0; 
}

Check an example here.

Bakudan
  • 19,134
  • 9
  • 53
  • 73
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 1
    This doesn't distinguish between caret positions when the caret is placed on an empty line. See http://stackoverflow.com/questions/3053542/how-to-get-the-start-and-end-points-of-selection-in-text-area/3053640#3053640 – Tim Down Jul 12 '10 at 00:07
  • 4
    This answer doesn't deal with the empty lines problem. – Tim Down Nov 15 '10 at 16:05
  • Can you also make one that sets the caret position? – trusktr Dec 14 '11 at 00:13
  • 6
    How can I use this for CONTENTEDITABLE div? – Muhammet Göktürk Ayan May 04 '12 at 07:36
  • Small addition to CMS answer. When in IE 7 this code returns -1 if the last character is space and the caret is after it. Checking return value does the trick in this case. – bybor Aug 12 '09 at 05:44
  • Hi, can anyone tell me what are the minimum versions of different browsers which will support the above code? – SexyBeast Jan 19 '13 at 20:00
  • 1
    You might want to reword this answer a little, `Safari (and other Gecko based browsers)` Seems to imply that Safari uses Gecko. Gecko is Mozilla's engine; Safari uses WebKit. – Useless Code Mar 29 '13 at 11:40
  • 1
    caret at position 0 would fail the test `if (el.selectionStart) { return el.selectionStart; }`... – okm Aug 07 '13 at 13:24
  • 1
    ... which should'nt really matter because in this case the method correctly returns 0 anyway. – connexo May 19 '15 at 15:18
57

Updated 5 September 2010

Seeing as everyone seems to get directed here for this issue, I'm adding my answer to a similar question, which contains the same code as this answer but with full background for those who are interested:

IE's document.selection.createRange doesn't include leading or trailing blank lines

To account for trailing line breaks is tricky in IE, and I haven't seen any solution that does this correctly, including any other answers to this question. It is possible, however, using the following function, which will return you the start and end of the selection (which are the same in the case of a caret) within a <textarea> or text <input>.

Note that the textarea must have focus for this function to work properly in IE. If in doubt, call the textarea's focus() method first.

function getInputSelection(el) {
    var start = 0, end = 0, normalizedValue, range,
        textInputRange, len, endRange;

    if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
        start = el.selectionStart;
        end = el.selectionEnd;
    } else {
        range = document.selection.createRange();

        if (range && range.parentElement() == el) {
            len = el.value.length;
            normalizedValue = el.value.replace(/\r\n/g, "\n");

            // Create a working TextRange that lives only in the input
            textInputRange = el.createTextRange();
            textInputRange.moveToBookmark(range.getBookmark());

            // Check if the start and end of the selection are at the very end
            // of the input, since moveStart/moveEnd doesn't return what we want
            // in those cases
            endRange = el.createTextRange();
            endRange.collapse(false);

            if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
                start = end = len;
            } else {
                start = -textInputRange.moveStart("character", -len);
                start += normalizedValue.slice(0, start).split("\n").length - 1;

                if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
                    end = len;
                } else {
                    end = -textInputRange.moveEnd("character", -len);
                    end += normalizedValue.slice(0, end).split("\n").length - 1;
                }
            }
        }
    }

    return {
        start: start,
        end: end
    };
}
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Excuse me but what does '*range && range.parentElement()*' mean? – sergzach Aug 19 '11 at 21:32
  • There is a problem if we want to get position of caret in IE (if selection is empty). In this case it returns *0* as start and *string length* as end (if we use *true* instead of *range && range.parentElement() == el*). – sergzach Aug 19 '11 at 21:55
  • @sergzach: The `range && range.parentElement() == el` is there to test whether the selection is within the textarea and is necessary. There is no problem with the function obtaining the caret position **so long as the textarea has the focus**. If unsure, call the textarea's `focus()` method before calling `getInputSelection()`. I'll add a note to the answer. – Tim Down Aug 20 '11 at 16:10
  • @Misha: I don't have access to IE 9 right now. What's the problem? – Tim Down Aug 25 '11 at 13:32
  • 1
    @Tim: When clicking on a div element to reveal the selection the "start" and the "end" are always the same. – Misha Moroshko Aug 25 '11 at 13:40
  • 3
    @Misha: That's not the fault of the function: that's what is actually selected by the time the function executes. You can see it visually after dismissing the alert box. As I mentioned in my answer to your recent question, two possible workarounds are using the `mousedown` event or adding `unselectable="on"` to the `
    ` element.
    – Tim Down Aug 25 '11 at 13:46
  • @Tim: Thanks a lot! The `unselectable="on"` option worked better in my case. You really helped me a lot! Keep providing an amazing support for your answers! – Misha Moroshko Aug 25 '11 at 14:05
  • @TimDown I'm getting an error "Uncaught TypeError: Cannot call method 'createRange' of undefined" in Google Chrome... Is this function only for IE? – trusktr Dec 13 '11 at 23:29
  • @trusktr: No, it works in all major browsers. If you're getting that error then you must be passing something other than a textarea or text input box into the function. – Tim Down Dec 14 '11 at 00:15
  • I'm only passing a textarea or text input, nothing else... It must be something else causeing the problem. I'll report when I find it... hmmmmm.... (I'm trying to use it for my Chrome plugin: https://chrome.google.com/webstore/detail/nljkbfcdjkldahnpklbkbdfojjcajfol) – trusktr Dec 29 '11 at 09:11
  • @TimDown - with external button event, scenario like this http://jsfiddle.net/kayz1/BwRC6/3/ (but this fiddle is not working in Opera, don't know why) – kayz1 Jun 27 '13 at 06:32
  • @kayz1: I don't know why it's not working in Opera either, but it's not a problem with this function. Nothing worked in that jsFiddle until I removed jQuery. – Tim Down Jun 27 '13 at 09:37
3

I modified the above function to account for carriage returns in IE. It's untested but I did something similar with it in my code so it should be workable.

function getCaret(el) {
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
    rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    var add_newlines = 0;
    for (var i=0; i<rc.text.length; i++) {
      if (rc.text.substr(i, 2) == '\r\n') {
        add_newlines += 2;
        i++;
      }
    }

    //return rc.text.length + add_newlines;

    //We need to substract the no. of lines
    return rc.text.length - add_newlines; 
  }  
  return 0; 
}
Community
  • 1
  • 1
mark
  • 31
  • 1
2

If you don't have to support IE, you can use selectionStart and selectionEnd attributes of textarea.

To get caret position just use selectionStart:

function getCaretPosition(textarea) {
  return textarea.selectionStart
}

To get the strings surrounding the selection, use following code:

function getSurroundingSelection(textarea) {
  return [textarea.value.substring(0, textarea.selectionStart)
         ,textarea.value.substring(textarea.selectionStart, textarea.selectionEnd)
         ,textarea.value.substring(textarea.selectionEnd, textarea.value.length)]
}

Demo on JSFiddle.

See also HTMLTextAreaElement docs.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177