javascripthtmlselectiontextselection

Coordinates of selected text in browser page


I need the coordinates in pixels of the beginning of the text selection (anywhere on the page, not in a textarea).

I tried using the cursor coordinates but this didn't work quite well because the cursor coordinates and the beginning of the selection are not always the same (for example when a user drags over a text).

I hope someone has the solution!


Solution

  • In IE >= 9 and non-IE browsers (Firefox 4+, WebKit browsers released since early 2009, Opera 11, maybe earlier), you can use the getClientRects() method of Range. In IE 4 - 10, you can use the boundingLeft and boundingTop properties of the TextRange that can be extracted from the selection. Here's a function that will do what you want in recent browsers.

    Note that there are some situations in which you may wrongly get co-ordinates 0, 0, as mentioned in the comments by @Louis. In that case you'll have to fall back to a workaround of temporarily inserting an element and getting its position.

    jsFiddle: http://jsfiddle.net/NFJ9r/132/

    Code:

    function getSelectionCoords(win) {
        win = win || window;
        var doc = win.document;
        var sel = doc.selection, range, rects, rect;
        var x = 0, y = 0;
        if (sel) {
            if (sel.type != "Control") {
                range = sel.createRange();
                range.collapse(true);
                x = range.boundingLeft;
                y = range.boundingTop;
            }
        } else if (win.getSelection) {
            sel = win.getSelection();
            if (sel.rangeCount) {
                range = sel.getRangeAt(0).cloneRange();
                if (range.getClientRects) {
                    range.collapse(true);
                    rects = range.getClientRects();
                    if (rects.length > 0) {
                        rect = rects[0];
                    }
                    x = rect.left;
                    y = rect.top;
                }
                // Fall back to inserting a temporary element
                if (x == 0 && y == 0) {
                    var span = doc.createElement("span");
                    if (span.getClientRects) {
                        // Ensure span has dimensions and position by
                        // adding a zero-width space character
                        span.appendChild( doc.createTextNode("\u200b") );
                        range.insertNode(span);
                        rect = span.getClientRects()[0];
                        x = rect.left;
                        y = rect.top;
                        var spanParent = span.parentNode;
                        spanParent.removeChild(span);
    
                        // Glue any broken text nodes back together
                        spanParent.normalize();
                    }
                }
            }
        }
        return { x: x, y: y };
    }
    

    UPDATE

    I submitted a WebKit bug as a result of the comments, and it's now been fixed.

    https://bugs.webkit.org/show_bug.cgi?id=65324