Finding selection start and end position in a textarea, in Internet Explorer

Published June 29th, 2005

[UPDATED 1st July 2005 -- a better method!]

I’m sure I’m missing something obvious here — I’m sure this can’t be true. If you can put me out of my misery then please do so. In the meantime…

I want to get the start and end positions of selected text in a TEXTAREA element. I don’t mean I want to manipulate the contents of the selection — you can do that easily like this:

document.selection.createRange().text = 'Some new text';

I need to know where in a piece of text the selection begins and ends. It’s easy enough in Mozilla: element.selectionStart and element.selectionEnd do the trick.

In IE, you can get just about every other bit of information you could possibly want — the position and size of the selection in pixels, for example — but not the start and end points of the selection. Bonkers.

I tried a number of ideas, and ended up with (yet another) ugly but functional fix. It goes like this:

  • Use document.selection.createRange() to get the current selection
  • Create a duplicate of that range with textRange.duplicate()
  • Use range.moveToElementText() to select all text in the duplicate range
  • Now use textRange.setEndPoint() to move the end of the range to the same place as the original range (ie. the original end point of the selection)
  • Subtract the length of the original selection from the length of the new selection, to get the start point of the original selection
  • Add the length of the original selection to the newly-calculated start point, to get the end point

Phew! Way too complicated for what should be a simple operation, but it does work.

Here’s some code:

var element = document.getElementById( 'my_textarea' );
if( document.selection ){
	// The current selection
	var range = document.selection.createRange();
	// We'll use this as a 'dummy'
	var stored_range = range.duplicate();
	// Select all text
	stored_range.moveToElementText( element );
	// Now move 'dummy' end point to end point of original range
	stored_range.setEndPoint( 'EndToEnd', range );
	// Now we can calculate start and end points
	element.selectionStart = stored_range.text.length - range.text.length;
	element.selectionEnd = element.selectionStart + range.text.length;
}

Because this sets the selectionStart and selectionEnd properties for the element, you can use the same methods for getting/setting selection contents as you would for Mozilla.

I made extensive use of this Microsoft reference page to work all this stuff out.

Get a Trackback link

1 Trackbacks/Pingbacks

  1. Pingback: Modificando un textarea desde javascript « Penyaskito on August 28, 2007

49 Comments

  1. Rafi B. on August 4, 2005

    Thank you for posting this helpful tip.
    I’ve been trying to use it on the input type=”text” element,
    but with no success. Is this just for a textarea element?

    I’ve found a solution though:

    function getCaretPos(el) {
    if (typeof el.selectionStart != “undefined”)
    return el.selectionStart;
    else if (document.selection)
    return Math.abs(document.selection.createRange().moveStart(“character”, -1000000));
    }

  2. Ratty on August 25, 2005

    This code seems to stop keyboard navigation working. ie. pressing the arrows to move around the text box do nothing. is there a fix for this?

  3. Ratty on August 25, 2005

    Sorry, I was wrong. It was my fault for only testing it under Wine in Linux. Works perfectly in Windows. Thanks dude :)

  4. ike on September 6, 2005

    well, your solution doesn`t work if i apply it to a body of a html… it goes close but….not close enough! the region seems shifted by a few characters. if the body does not have tags it works wonderfully but i need a solution for full html documents. any ideas? i`ve tried to eliminate the tags from region.htmlText and count it like that but it still doesn`t work. i`m desperate.

  5. Sebastian Werner on September 29, 2005

    The length of the current selection you can get from:

    document.selection.createRange().text.length;

    Then you only need to detect where the start point is, to get the matching end.

    var yourrange = yourtextfield.createTextRange();
    yourrange.setEndPoint(“EndToStart”, document.selection.createRange());
    var yourselstart = yourrange.text.length;

    Just my two cents. But your solution is not so much different.

  6. Sébastien CRAMATTE on November 6, 2005

    I need to handle selection with IE on keyPress event ? have you got an idea or some code snippet ?

  7. Tobias on December 11, 2005

    The shown code only works for textareas but not for input fields of type ‘text’. Therefore, the line

    “stored_range.moveToElementText( element );”

    can be replaced by

    “stored_range.expand(‘textedit’);”

    This works for me

  8. Abhishek Iyer on December 29, 2005

    Pity that there couldnt be a simpler soln. In the meantime , might i add that this shlould be handled in “onMouseDown” else you might loose the selection. Great work StickMan!

  9. suraj on April 21, 2006

    this guy is the fucking MAN! fucking LIFE SAVER…

    p.s. – these security codes are REALLY TOO difficult to read :S

  10. suraj on April 21, 2006

    after experimenting, this code seems to struggle in textareas…

    For the first line – it works fine to reposition the caret however, as you move further down the textarea it runs into problems and adds the caret to a silly position

  11. Stine on August 3, 2006

    Hi:) This seems to work for me:

    function js_countTextAreaChars(text) {
        var n = 0;
        for (var i = 0; i

  12. Stine on August 3, 2006

    Maybe it’s not possible to post more than a few lines? I’ll try again:

    function js_countTextAreaChars(text) {
        var n = 0;
        for (var i = 0; i

  13. Stine on August 3, 2006

    function js_countTextAreaChars(text) {
    var n = 0;
    for (var i = 0; i

  14. Stine on August 3, 2006

    Well, I give up;)

  15. Stine on August 3, 2006

    function js_countTextAreaChars(text) {
        var n = 0;
        for (var i = 0; i < text.length; i++) {
            if (text.charAt(i) != ‘\r’) {
                n++;
            }
        }
        return n;
    }

    function js_CursorPos(start, end) {
        this.start = start;
        this.end = end;
    }

    function js_getCursorPosition(textArea) {
        var start = 0;
        var end = 0;
        if (document.selection) { // IE…
            textArea.focus();
            var sel1 = document.selection.createRange();
            var sel2 = sel1.duplicate();
            sel2.moveToElementText(textArea);
            var selText = sel1.text;
            sel1.text = “01″;
            var index = sel2.text.indexOf(“01″);
            start = js_countTextAreaChars((index == -1) ? sel2.text : sel2.text.substring(0, index));
            end = js_countTextAreaChars(selText) + start;
            sel1.moveStart(‘character’, -1);
            sel1.text = selText;
        } else if (textArea.selectionStart || (textArea.selectionStart == “0″)) { // Mozilla/Netscape…
            start = textArea.selectionStart;
            end = textArea.selectionEnd;
        }
        return new js_CursorPos(start, end);
    }

    function js_setCursorPosition(textArea, cursorPos) {
        if (document.selection) { // IE…
            var sel = textArea.createTextRange();
            sel.collapse(true);
            sel.moveStart(“character”, cursorPos.start);
            sel.moveEnd(“character”, cursorPos.end – cursorPos.start);
            sel.select();
        } else if (textArea.selectionStart || (textArea.selectionStart == “0″)) { // Mozilla/Netscape…
            textArea.selectionStart = cursorPos.start;
            textArea.selectionEnd = cursorPos.end;
        }
        textArea.focus();
    }

  16. Stine on August 3, 2006

    There it is;)

  17. Original Sin on November 23, 2006

    Very sweet piece of code. I changed the Element to be a div tag, and it worked like a treat when combined with Javascript that fetches your selection. Thanks!

  18. AmigoJack on November 28, 2006

    Stines code doesnt work for me. it inserts “0″ into the text where my caret is. plus: if somewhere within the text “01″ occurs before, it doesnt work as expected of course :(

    not to mention what will happen if the textarea isnt the only input on the document… *ARGH* the MSIE is really painful when it comes down on determining selection positions

  19. Jayakrishnan G on April 16, 2007

    What about licensing terms for using the above script.

    Is the permission, to use this code, under the terms of GNU LGPL (www.gnu.org/licenses/lgpl.html) ?

  20. roncli on May 3, 2007

    AmigoJack,

    - There’s a bug in Stine’s code. In the line — sel1.moveStart(’character’, -1); — the number “-1″ should be equal to zero minus the length of the dummy text. In the case of his code, the dummy text is “01″, so the number should be changed to “-2″.

    - You don’t have to use “01″ as your dummy text, just change it to something that user’s won’t be likely to use, like “|pos|”, as the pipes aren’t something users often type in. Of course, you’d now have to change the “-1″ in my above point to the length of that dummy text, which is a three letter word plus two pipes… that makes it “-5″.

  21. Tiny on May 4, 2007

    quicker than Stine’s countTextAreaChars() one can, for textarea text say:

    var iLen=text.length-text.split(‘\r’).length-1;

    This will account for IE’s funny way of *sometimes* counting \r

    Also, instead of adding “01″ or “|pos|” try adding something truely obscure like

    String.fromCharCode(28)

    There is a character that your users will have a hard time typing! …And also, it works well for me. I’m not using Stine’s method, or even StickMan’s, but one like it. Since IE also insists on ignoring newlines that appear at the end of my text selections (anyone else experience that?) I’ve efforted to make sure there would be a non-newline at the end of my textarea, but also a character that would not confuse the user. I found decimal 28 (octal 34) to be good for that, then, I measure my positions from the end, because IE does count leading newlines.

    if (oTextarea.value.charCodeAt(oTextarea.value.length-1) < 14) {
    oTextarea.value=oTextarea.value.replace(/34/g,”)+String.fromCharCode(28);
    }
    var oRng = document.selection.createRange();
    var oRng2 = oRng.duplicate();
    oRng2.moveToElementText(oTextarea);
    oRng2.setEndPoint(‘StartToEnd’, oRng);
    iEnd = oTextarea.value.length-oRng2.text.length;
    oRng2.setEndPoint(‘StartToStart’, oRng);
    iStart = oTextarea.value.length-oRng2.text.length;

  22. Tiny on May 5, 2007

    by
    var iLen=text.length-text.split(’\r’).length-1;
    I of course meant
    var iLen=text.length-(text.split(’\r’).length-1);

  23. Tiny on May 5, 2007

    and the regex

    /34/g reads (let me try this again) /\34/g

  24. Tiny on May 5, 2007

    and trying one more time

    / \ 0 3 4 / g

    (take out the spaces)
    its too bad the comment preview doesn’t show us how special characters are handled.

  25. Lázaro on August 16, 2007

    Thanks for this solution. I spent many time in that Microsoft’s page, but I couldn’t find anything, unitl I “fall” here…thanks again.

  26. arash on December 9, 2007

    is there a solution to get cursor position coordinates in a input text html tag?
    i have tooltip which should be always above the cursor. and where ever cursor moves (by typing,arrowkeys or mouse) it must move.

  27. Vikrant Mohite on March 25, 2008

    I want to split(“\n”) my textarea at charAt(50). can you guys help me out for the same.

  28. Steve on May 6, 2008

    Nice article, but your code ist difficult to read. Thanks for peoples for useful information :)

  29. Stickman on May 6, 2008

    Thanks for the heads-up, Steve. Looks like the ‘code’ tag I’ve been using doesn’t really work properly any more, for some reason. I’ve fixed the formatting for this article, now I need to find any other articles that have similar problems.

  30. yohi on June 12, 2008

    dear Stine,

    your 15 th posting is very effective , but , when text area is having scroll box the code doesn’t focus on correct scroll position . plz give me a solution

    thanks
    yohi

  31. nayana adassuriya on July 25, 2008

    Thanks Stine, ur code is very use full, good work keep posting!!!!

  32. Andrew on September 18, 2008

    Thanks for this very useful function. I don’t know or even care how it works, but it works! Its ridiculous that ie doesn’t support selectionStart and selectionEnd properties.

  33. anon on October 1, 2008

    Solved elegantly at
    http://www.webdeveloper.com/forum/archive/index.php/t-74982.html

    m2pc
    08-24-2005, 01:48 PM

    var oSel = document.selection.createRange ();

    // Move selection start to 0 position
    oSel.moveStart (‘character’, -oField.value.length);

    // The caret position is selection length
    iCaretPos = oSel.text.length;

  34. Mau on October 16, 2008

    Thank you so much for this trick. It works great for me.
    Chreers!

  35. Thomas on February 26, 2009

    Thank you so much. I was going crazy trying to fix this and then I found your code. It works perfectly!! :)

  36. Colin on May 22, 2009

    Curious, how would one do the opposite – set the caret position?

  37. MeltingIce on May 26, 2009

    Just wanted to say THANK YOU SO MUCH. It’s almost 5am and I was about to go officially insane when I finally found this post and it saved me from breaking the closest thing within my reach XD

    Works beautifully for me!

  38. Louis Hoefler on July 22, 2009

    getSelectionOffset : function(argObject) {
    if (typeof(argObject.contentWindow.getSelection) != ‘undefined’) { //Moz
    return {
    start: argObject.contentWindow.getSelection().getRangeAt(0).selectionStart,
    end: argObject.contentWindow.getSelection().getRangeAt(0).selectionEnd
    }
    }
    if (document.selection && document.selection.createRange) { //IE
    var allText = argObject.contentWindow.document.selection.createRange().parentElement().innerText;
    var selText = argObject.contentWindow.document.selection.createRange().text;
    return {
    start: allText.indexOf(selText),
    end: allText.indexOf(selText) + selText.length
    }
    }
    }

  39. Scott Alex on August 29, 2009

    Here is a better solution, supported by IE 4.0+

    // Current selection
    var range = document.selection.createRange();

    if(range.text != “”) {

    // Calculate start and end points
    sourceSelEnd = range.boundingLeft;
    sourceSelStart = sourceSelEnd – range.text.length;
    sourceSelected = range.text;

    }

    Documentation:
    http://msdn.microsoft.com/en-us/library/ms533539(VS.85).aspx

  40. Apanatshka on January 14, 2010

    Thanks a lot for the script Stickman! :D I’ve adapted it a bit to automatically update the selectionStart and selectionEnd. It does depend on the jquery library, but it can be written so it doesn’t depend on jquery. Anyway, adding this file with condition comments works for me.

    /*This javascript depends upon the jquery-1.3.2.js library

    The content of this script sets a Selectionevent on every textarea in the document to update their selectionStart and selectionEnd. This is an IE hack to be able to use the normal selectionStart and selectionEnd Mozilla Firefox already has. The actual script to get the selection in IE is not mine! A person I only know as Stickman wrote it. This is the url: http://the-stickman.com/web-development/javascript/finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
    */

    $(document).ready(function(){
    $(‘textarea’).select(function(){
    if(document.selection){
    var range = document.selection.createRange();
    var stored_range = range.duplicate();
    stored_range.moveToElementText(this);
    stored_range.setEndPoint(‘EndToEnd’, range);
    this.selectionStart = stored_range.text.length – range.text.length;
    this.selectionEnd = this.selectionStart + range.text.length;
    };
    })
    });

  41. surendra on March 18, 2010

    But Those are not working in all browsers except IE

  42. Nux on April 10, 2010

    Just a note as this is high in Google…

    boundingLeft will NOT work at all in this situation – it gives a value in pixels not characters.

    Stine version is also wrong as the text it self might contain “01″.

    The original version seems to work almost perfectly, but I think setting the attributes is not a good idea as they won’t be updated and some script might use them to detect compatibility.

    I said “almost perfectly”, because to get a caret position you should run:
    element.focus()

  43. Jerry B. on May 12, 2010

    Here is my solution, it moves a bookmark from the document.selection textrange to an element textrange. It correctly calculates the start and end positions regardless of where the element is in the page.

    function getSelectionRange(oElm)
    {
    var $r = { text: “”, start: 0, end: 0, length: 0 };
    if (oElm.setSelectionRange)
    { // W3C/Gecko
    $r.start= oElm.selectionStart;
    $r.end = oElm.selectionEnd;
    $r.text = ($r.start != $r.end) ? oElm.value.substring($r.start, $r.end): “”;
    }
    else if (document.selection)
    { // IE
    if (oElm.tagName && oElm.tagName === “TEXTAREA”)
    {
    var $oS = document.selection.createRange().duplicate();
    var $oR = oElm.createTextRange();
    var $sB = $oS.getBookmark();
    $oR.moveToBookmark($sB);
    }
    else
    var $oR = document.selection.createRange().duplicate();
    $r.text = $oR.text;
    for (; $oR.moveStart(“character”, -1) !== 0; $r.start++);
    $r.end = $r.text.length + $r.start;
    }
    $r.length = $r.text.length;
    return $r;
    }

  44. anonymous on May 25, 2010

    pretty helpfull…thanks a ton!!

  45. Stephen Andrew Carter on June 3, 2010

    function get_selection(the_id)
    {
    var e = document.getElementById(the_id);

    //Mozilla and DOM 3.0
    if(‘selectionStart’ in e)
    {
    var l = e.selectionEnd – e.selectionStart;
    return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) };
    }
    //IE
    else if(document.selection)
    {
    e.focus();
    var r = document.selection.createRange();
    var tr = e.createTextRange();
    var tr2 = tr.duplicate();
    tr2.moveToBookmark(r.getBookmark());
    tr.setEndPoint(‘EndToStart’,tr2);
    if (r == null || tr == null) return { start: e.value.length, end: e.value.length, length: 0, text: ” };
    var text_part = r.text.replace(/[\r\n]/g,’.'); //for some reason IE doesn’t always count the \n and \r in the length
    var text_whole = e.value.replace(/[\r\n]/g,’.');
    var the_start = text_whole.indexOf(text_part,tr.text.length);
    return { start: the_start, end: the_start + text_part.length, length: text_part.length, text: r.text };
    }
    //Browser not supported
    else return { start: e.value.length, end: e.value.length, length: 0, text: ” };
    }

    function replace_selection(the_id,replace_str)
    {
    var e = document.getElementById(the_id);
    selection = get_selection(the_id);
    var start_pos = selection.start;
    var end_pos = start_pos + replace_str.length;
    e.value = e.value.substr(0, start_pos) + replace_str + e.value.substr(selection.end, e.value.length);
    set_selection(the_id,start_pos,end_pos);
    return {start: start_pos, end: end_pos, length: replace_str.length, text: replace_str};
    }

    function set_selection(the_id,start_pos,end_pos)
    {
    var e = document.getElementById(the_id);

    //Mozilla and DOM 3.0
    if(‘selectionStart’ in e)
    {
    e.focus();
    e.selectionStart = start_pos;
    e.selectionEnd = end_pos;
    }
    //IE
    else if(document.selection)
    {
    e.focus();
    var tr = e.createTextRange();

    //Fix IE from counting the newline characters as two seperate characters
    var stop_it = start_pos;
    for (i=0; i < stop_it; i++) if( e.value[i].search(/[\r\n]/) != -1 ) start_pos = start_pos – .5;
    stop_it = end_pos;
    for (i=0; i < stop_it; i++) if( e.value[i].search(/[\r\n]/) != -1 ) end_pos = end_pos – .5;

    tr.moveEnd('textedit',-1);
    tr.moveStart('character',start_pos);
    tr.moveEnd('character',end_pos – start_pos);
    tr.select();
    }
    return get_selection(the_id);
    }

    function wrap_selection(the_id, left_str, right_str, sel_offset, sel_length)
    {
    var the_sel_text = get_selection(the_id).text;
    var selection = replace_selection(the_id, left_str + the_sel_text + right_str );
    if(sel_offset !== undefined && sel_length !== undefined) selection = set_selection(the_id, selection.start + sel_offset, selection.start + sel_offset + sel_length);
    else if(the_sel_text == '') selection = set_selection(the_id, selection.start + left_str.length, selection.start + left_str.length);
    return selection;
    }

    //Your welcome :)

  46. Chandu on June 8, 2010

    Hi,

    document.selection.createRange(); not working for textbox. is there any alternative for it?

    Regards,
    Chandu

  47. Simon Sanderson on June 9, 2010

    Hi Jerry – Your solution is perfect … almost. Unfortunatley I cannot see why it doesn’t work in this one case.

    For IE6: if I have a textarea such as

    ABC

    and I call your function for every move of the cursor I get the following debug trace in my software:

    Selection mode mode (1 char at a time)
    Start(0), End(1), Length(1), Text(A)
    Start(1), End(2), Length(1), Text(B)
    Start(2), End(3), Length(1), Text(C)

    Non selection mode (simply pressing right arrow)
    Start(0), End(0), Length(0), Text()
    Start(1), End(1), Length(0), Text()
    Start(2), End(2), Length(0), Text()
    Start(1), End(1), Length(0), Text()

    as you can possible see from the last line when I the cursor is to the right of the ‘C’ I would expect the output to read
    Start(3), End(3), Length(0), Text()

    This is what happens in Firefox (albeit through a different branch of the function)

    Any ideas how to modify the code to correct this small inconsistency?

  48. plr on October 19, 2010

    Hi, what if i want to modify a SPECIFIC text in the textarea, and not modify the whole text area value? Can this be done?

  49. Jerry B. on July 14, 2011

    I revised my solution to handle the issue noted by Simon Sanderson.

    It moves a bookmark from the document.selection textrange to an element textrange. It correctly calculates the start and end positions regardless of where the element is in the page. It now handles end of field bookmarks with no selection.

    function getSelectionRange(oElm)
    {
    var $r = { text: “”, start: 0, end: 0, length: 0 };
    if (oElm.setSelectionRange)
    { /* W3C/Gecko/IE9+ */
    $r.start= oElm.selectionStart;
    $r.end = oElm.selectionEnd;
    $r.text = ($r.start !== $r.end) ? oElm.value.substring($r.start, $r.end): “”;
    }
    else if (document.selection)
    { /* IE8- */
    var $oS, $oR, $oT;
    if (oElm.tagName && oElm.tagName === “TEXTAREA”)
    {
    $oS = document.selection.createRange().duplicate();
    $oR = oElm.createTextRange();
    $oR.collapse(false);
    $oR.moveToBookmark($oS.getBookmark());
    if ($oS.text === “”)
    {
    $oT = $oR.duplicate();
    $oT.moveEnd(“character”, 1);
    if ($oS.boundingWidth === $oT.boundingWidth && $oS.boundingHeight === $oT.boundingHeight)
    {
    $oR = $oT;
    }
    }
    }
    else
    {
    $oR = document.selection.createRange().duplicate();
    }
    $r.text = $oR.text;
    $r.start = Math.abs($oR.moveStart(“character”, -1000000));
    $r.end = $r.text.length + $r.start;
    }
    else if (document.getSelection)
    { /* Netscape 4 */
    $r.text = document.getSelection();
    $r.end = $r.text.length;
    }
    $r.length = $r.text.length;
    return $r;
    }

Leave a comment

Comment Policy: First time comments are moderated. Please be patient.

OpenID

Anonymous