How to get the start and end points of selection in text area?

Revised answer, 5 September 2010

To account for trailing line breaks is tricky in IE, and I haven't seen any solution that does this. It is possible, however. The following is a new version of what I previously posted here.

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
    };
}

Use the Rangy api and all of your problems are gone gone gone gone...

Using it

Read the documentation, or just use the below.
Very simple,

var selection = rangy.getSelection(),  // Whole lot of information, supports
                                       // multi-selections too.
    start   = selection.anchorOffset,  // Start position
    end     = selection.focusOffset;   // End position

Hope this api helps you out because it is really helpful in handling cross-browser ranges.


I needed to do something very similar and came up with this:

function getSelection(target) {
    var s = {start: 0, end:0};
    if (typeof target.selectionStart == "number"
        && typeof target.selectionEnd == "number") {
        // Firefox (and others)
        s.start = target.selectionStart;
        s.end = target.selectionEnd;
    } else if (document.selection) {
        // IE
        var bookmark = document.selection.createRange().getBookmark();
        var sel = target.createTextRange();
        var bfr = sel.duplicate();
        sel.moveToBookmark(bookmark);
        bfr.setEndPoint("EndToStart", sel);
        s.start = bfr.text.length;
        s.end = s.start + sel.text.length;
    }
    return s;
}

Notes:

  • The sel range must be created by target rather than using the range returned by document.selection, otherwise bfr.setEndPoint will complain about an invalid argument. This "feature" (discovered in IE8) does not appear to be documented in the spec.
  • target must have input focus for this function to work.
  • Only tested with <textarea>, might work with <input> as well.