17

I want to get the cursor start and end position of a selected range in a text-field or text-area.

I tried lot of functions in various forums, but when the last character of the selection is a new line character JavaScript ignore it in IE6.

How do I get the start and end points of the selection?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Nayana Adassuriya
  • 171
  • 1
  • 1
  • 3

3 Answers3

34

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
    };
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • thank you very much Tim Down, i already tried this thread. but the problem is when the last character of the selection is a new line character JavaScript ignore it in IE. otherwise those functions working fine – Nayana Adassuriya Jun 16 '10 at 15:03
  • Answer now completely rewritten. – Tim Down Jun 17 '10 at 10:55
  • thank you very much Tim Down, this method working fine. for simple cursor position. but if we select a text range for get the start position of the selected text. that selected text automatically deleting. so how can i correct this. any way this is a nice trick and u make a gr8 invention. – Nayana Adassuriya Jun 17 '10 at 12:14
  • Yes, that's the same problem that I spotted. I've fixed it now. – Tim Down Jun 17 '10 at 13:26
  • Ohooo gr8 it is working fine!!! thank you very much Tim Down, and another help for me!!. i want to fine the end point of the selection as well in same function. How can i implement that requirment. – Nayana Adassuriya Jun 18 '10 at 04:39
  • I've added a parameter to specify start or end position. – Tim Down Jun 20 '10 at 21:04
  • How can I use this function? –  Oct 25 '13 at 19:08
7

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.

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • Rangy is great, but it doesn't seem to work for text areas and text inputs. For that there's rangyinputs (a jQuery plugin): https://code.google.com/p/rangyinputs/ – newenglander Jun 17 '14 at 10:20
  • Hey, is there any way to use Rangy without installing node then npm then bower then Rangy? Can I just include a minified js file in my website? – Edward Newell Mar 27 '15 at 17:07
  • 1
    @EdwardNewell I believe you can. Try using the core file [here](https://github.com/timdown/rangy/tree/master/lib) and see if it works. IIRC this is how I used it before it was being moved to github. For additional features just apply the different modules. – Derek 朕會功夫 Mar 27 '15 at 18:14
6

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.
millerdev
  • 10,011
  • 2
  • 31
  • 27