How to get the caret column (not pixels) position in a textarea, in characters, from the start?
Asked Answered
A

4

179

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'.

Antibody answered 4/11, 2008 at 22:36 Comment(4)
See this question: #164647 and if you will have newlines, also the note about that here: #235911Anesthesiologist
If you are using jQuery you can use jquery caret plugin $('textarea').getSelection().start plugins.jquery.com/plugin-tags/caret @++Trouper
Found a good solution at blog.vishalon.net/index.php/… I tested it in firefox and chrome, and it worked in both. The writer says it works in IE+Opera as well.Sonnie
Simple use textarea.selectionStart, textarea. selectionEnd, textarea.setSelectionRange developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElementBuitenzorg
B
174

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.

Booty answered 4/11, 2008 at 22:58 Comment(9)
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.Ecchymosis
This doesn't distinguish between caret positions when the caret is placed on an empty line. See #3054042Mohandis
This answer doesn't deal with the empty lines problem.Mohandis
Can you also make one that sets the caret position?Buggs
How can I use this for CONTENTEDITABLE div?Offshoot
Hi, can anyone tell me what are the minimum versions of different browsers which will support the above code?Balcony
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.Tita
caret at position 0 would fail the test if (el.selectionStart) { return el.selectionStart; }...Flatways
... which should'nt really matter because in this case the method correctly returns 0 anyway.Novel
M
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
    };
}
Mohandis answered 30/7, 2010 at 15:38 Comment(12)
Excuse me but what does 'range && range.parentElement()' mean?Chubb
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).Chubb
@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.Mohandis
@Misha: I don't have access to IE 9 right now. What's the problem?Mohandis
@Tim: When clicking on a div element to reveal the selection the "start" and the "end" are always the same.Rom
@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 <div> element.Mohandis
@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!Rom
@TimDown I'm getting an error "Uncaught TypeError: Cannot call method 'createRange' of undefined" in Google Chrome... Is this function only for IE?Buggs
@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.Mohandis
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: chrome.google.com/webstore/detail/…)Buggs
@TimDown - with external button event, scenario like this jsfiddle.net/kayz1/BwRC6/3 (but this fiddle is not working in Opera, don't know why)Louella
@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.Mohandis
J
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; 
}
Jedthus answered 29/4, 2010 at 7:58 Comment(0)
Z
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.

Zap answered 25/12, 2015 at 22:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.