2

I can already do this for safari and IE like in this fiddle: http://jsfiddle.net/cyk8y/ (The code is a bit complicated or/and messy but it's just so you can see the final result I want).

I took the principal code from here: Set a selection range from A to B in absolute position

In the comments of the answer, Tim Down gave me these links: How to get a word under cursor using JavaScript and Creating a collapsed range from a pixel position in FF/Webkit to help me making my code working in FF and maybe Google Chrome.

I tried, but I didn't succeed.

Can someone give me an exemple that select from pixel coordinates that works in FF and/or Google Chrome (and Opera?).

Community
  • 1
  • 1
user1365010
  • 3,185
  • 8
  • 24
  • 43
  • I'm working on it. Latest unfinished-but-sort-of-working version: http://jsfiddle.net/timdown/ABjQP/2/ – Tim Down Jun 28 '12 at 00:07
  • @TimDown It's **SO** impressive ! Thank you a lot !!!!! – user1365010 Jun 28 '12 at 17:12
  • Seems to work in Opera for me. I know Opera supports the `getClientRects()` method of Range and `document.elementFromPoint()`, so it should work. – Tim Down Jun 28 '12 at 21:37
  • It's really strange. The first time I was using opera just after downloading it, it didn't work. Now, a day after, each time I try, it works. (Seems like a bug for the first time we use Opera - for Mac). In conclusion, it works for Opera. – user1365010 Jun 29 '12 at 08:18
  • In http://jsfiddle.net/timdown/ABjQP/2, did you add the code from http://stackoverflow.com/questions/11191136/set-a-selection-range-from-a-to-b-in-absolute-position or it's just the way for ff and chrome and opera? (Because I wanted to try if the version just with `getClientRects` and `elementFromPoint` works in safari and ie) – user1365010 Jun 29 '12 at 08:21
  • I added the code from that question and used the `getClientRects()` etc. stuff as a fallback, because it will be much slower and possible less accurate. Older IE (< 9) doesn't have a Range implementation or `document.elementFromPoint()`, so it won't work in those browsers. There's still a bit of work I need to do on it. – Tim Down Jun 29 '12 at 08:35
  • @TimDown: I have had some (negative) experience with that in the past (getting it working) and just wanted to say: **respect** – David Mulder Jun 29 '12 at 14:53
  • @TimDown - +1 for bacon ipsum! :) – chrisfrancis27 Jul 02 '12 at 14:27
  • @TimDown Where have you got to ? :) – user1365010 Jul 04 '12 at 08:20
  • Hello! I've been a bit busy. I'm still planning to finish off this code, don't worry. – Tim Down Jul 04 '12 at 08:28

1 Answers1

3

Here's the latest attempt of mine to do this. It seems superficially to work, but I'm making no guarantees: it's a non-trivial chunk of code and I haven't tested it thoroughly.

It may well end up in my Rangy library in some form once I've tidied and tested it.

Live demo: http://jsfiddle.net/timdown/ABjQP/8/

Extract of the code (the Firefox and Opera bit):

function getNodeIndex(node) {
    var i = 0;
    while( (node = node.previousSibling) ) {
        i++;
    }
    return i;
}

function getLastRangeRect(range) {
    var rects = range.getClientRects();
    return (rects.length > 0) ? rects[rects.length - 1] : null;
}

function pointIsInOrAboveRect(x, y, rect) {
    return y < rect.bottom && x >= rect.left && x <= rect.right;
}

function positionFromPoint(doc, x, y, favourPrecedingPosition) {
    var el = doc.elementFromPoint(x, y);

    var range = doc.createRange();
    range.selectNodeContents(el);
    range.collapse(true);

    var offsetNode = el.firstChild, offset, position, rect;

    if (!offsetNode) {
        offsetNode = el.parentNode;
        offset = getNodeIndex(el);
        if (!favourPrecedingPosition) {
            ++offset;
        }
    } else {
        // Search through the text node children of el
        main: while (offsetNode) {
            if (offsetNode.nodeType == 3) {
                // Go through the text node character by character
                for (offset = 0, textLen = offsetNode.length; offset <= textLen; ++offset) {
                    range.setEnd(offsetNode, offset);
                    rect = getLastRangeRect(range);
                    if (rect && pointIsInOrAboveRect(x, y, rect)) {
                        // We've gone past the point. Now we check which side
                        // (left or right) of the character the point is nearer to
                        if (rect.right - x > x - rect.left) {
                            --offset;
                        }
                        break main;
                    }
                }
            } else {
                // Handle elements
                range.setEndAfter(offsetNode);
                rect = getLastRangeRect(range);
                if (rect && pointIsInOrAboveRect(x, y, rect)) {
                    offset = getNodeIndex(offsetNode);
                    offsetNode = el.parentNode;
                    if (!favourPrecedingPosition) {
                        ++offset;
                    }
                    break main;
                }
            }

            offsetNode = offsetNode.nextSibling;
        }
        if (!offsetNode) {
            offsetNode = el;
            offset = el.childNodes.length;
        }
    }

    return {
        offsetNode: offsetNode,
        offset: offset
    };
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • I just saw a bug, it's when you write something like `createSelectionFromPoint(100, 100, 30, 30);` (when the selection is from down to top). Maybe you could solve that by doing `if(endY – user1365010 Jul 04 '12 at 23:59
  • @user1365010: Oh, you want to do a backward selection? Fair enough, that's not too hard, but adds more complexity and won't work in IE (it's impossible to create a backward selection programmatically in IE). Revising... – Tim Down Jul 05 '12 at 00:04
  • Yes, but you can simulate this by doing a normal selection but with inverted coordinates – user1365010 Jul 05 '12 at 00:05
  • @user1365010: Selections do have a direction. It affects arrow key behaviour, for example. – Tim Down Jul 05 '12 at 00:11
  • That's SO cool, thanks again! Something funny I could do with that would be to select something while the user want to select another thing :P – user1365010 Jul 05 '12 at 00:17