57

Currently I'm getting the selected text in the browser doing this:

window.getSelection();

Now I need to show a tooltip above that text when pressing a custom key(note that the mouse could not be over the text anymore), so in order to do that I need the absolute position of that selected text.

Is there a way to do that, maybe wrapping that text inside a tag and then getting the offsets? It just has to work in Chrome, not all browsers.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Ecarrion
  • 4,940
  • 1
  • 33
  • 44
  • 3
    Possible duplicate of [How can I position an element next to user text selection?](https://stackoverflow.com/questions/1589721/how-can-i-position-an-element-next-to-user-text-selection) – Liam May 24 '17 at 07:52

3 Answers3

87
s = window.getSelection();

Returns a Selection. So try

s = window.getSelection();
oRange = s.getRangeAt(0); //get the text range
oRect = oRange.getBoundingClientRect();

oRect will be the bounding rectangle in client (fixed) coordinates.

Jerinaw
  • 5,260
  • 7
  • 41
  • 54
  • The `getBoundingClientRect()` method of Range is not universally supported, unfortunately, although it's getting there. I don't think IE9 has it, for example. – Tim Down Jul 26 '13 at 22:17
  • 6
    The question says for Chrome only. This works in Chrome and FF. – Jerinaw Aug 04 '13 at 00:32
  • 7
    `oRange` ... I see what you did there :) – volter9 Jul 30 '15 at 04:01
  • 5
    Take a look here, the browser support is actually very good: http://caniuse.com/#feat=getboundingclientrect – Hooman Askari Nov 26 '15 at 20:37
  • @HumanA. The support should be for Range.getBoundingClientRect(), https://developer.mozilla.org/en-US/docs/Web/API/Range/getBoundingClientRect , the link says about getBoundingClientRect in general. – Suraj Jain Apr 23 '18 at 13:38
  • This won't work for a selection within a ``. – ADJenks Jan 25 '22 at 21:51
15

The easiest way is to insert a temporary marker element at the start or end of the selection and get its position. I've demonstrated how to do this before on Stack Overflow: How can I position an element next to user text selection?

Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 4
    If this is a duplicate of that question then shouldn't you vote to close? Not add an answer that points at another answer? – Liam May 24 '17 at 07:53
  • 3
    @Liam: I'm not sure; the questions are not identical but the technique needed to answer them is the same. I can't deny that I was in my maximal rep-hunting phase at the time of answering. – Tim Down May 24 '17 at 13:36
1

Before using getBoundingClientRect, you need to know this note:

CSSOM working draft specifies that it returns a ClientRect for each border box

And by this 'standard':

For an inline element, the two definitions are the same. But for a block element, Mozilla will return only a single rectangle.

So if anyone reading this post wants a general solution for more precise positions and layouts of selected texts, I suggest the following approaches:

Option 1: Find exact starting and and ending point of texts by inserting invisible elements. Then calculate selected line rectangles with extracted computed line height and container width. APIs in use: window.getComputedStyle.

  • Pro: the result would be most precise for each line of text.
  • Con: 1) If the selection is across several nodes with different line heights and widths, the algorithm becomes complex. 2) And you need to implement the computation algorithm, which is too time consuming when implementing a simple feature.

Option 2: Wrap each text with a carefully styled inline element, extracting layout of each box, and merge results into lines.

  • Pro: Works for all continuous selections (that basically means all cases in current mainstream browser implementations.). Good enough precision for each line of texts.
  • Con: 1) Its result is a little inaccurate in some cases, as it adds error widths of kerning. 2) It's slow on very large selection of texts.

For option 2, rangeblock is an existing implementation with an easy API which gives you the absolution layout of each line of text:

let block = rangeblock.extractSelectedBlock(window, document);
console.info("Text layout: " + JSON.stringify(block.dimensions));
// output: Text layout: {Left: 100, Top: 90, Width: 200, Height: 50}
Community
  • 1
  • 1
lowatt
  • 363
  • 2
  • 18