38

If I have an element, for instance:

<p>Lorem ipsum dolor sit amet, ante risus massa adipiscing quisque quis. At mauris tellus in, sed vehicula integer fermentum rhoncus at faucibus, in vitae purus. Maecenas in vel ligula orci tellus ac, fringilla conubia lorem elit. Dui nulla sodales morbi vel. Massa sed viverra. Maecenas imperdiet donec urna a, ligula sed</p>

And this is flowed across multiple lines:

Lorem ipsum dolor sit amet, ante risus massa adipiscing
quisque quis. At mauris tellus in, sed vehicula integer
fermentum rhoncus at faucibus, in vitae purus. Maecenas
in vel ligula orci tellus ac, fringilla conubia lorem
elit. Dui nulla sodales morbi vel. Massa sed viverra.
Maecenas imperdiet donec urna a, ligula sed

Is it possible to find out the position (in x,y-coordinates) of a particular character in the text using Javascript? If not, could I get the y-position of each line in the text?

Please note: There is a lot of text in the <p> tag in my application, so adding <span> around each character/word would be too much processing.

Flip
  • 6,233
  • 7
  • 46
  • 75
Jamie
  • 2,245
  • 4
  • 19
  • 24
  • 1
    You're saying you need offsets for every character + word rather than just a few specific ones? – Rup Feb 28 '11 at 15:10
  • @Rup good comment. I actually only need the y-position of each line in the text. Although since it's flowable text don't suppose it really makes a difference. Updated the question anyway. – Jamie Feb 28 '11 at 15:16
  • Perhaps this helps you: http://blog.mastykarz.nl/measuring-the-length-of-a-string-in-pixels-using-javascript/ – pimvdb Feb 28 '11 at 15:20
  • 1
    @pimvdb but that's just performing the span trick below but as a hidden element to measure width. I don't think that's easily applicable to this - except maybe you could fractions of the p to roughly split it into lines and mark spans on each line I suppose – Rup Feb 28 '11 at 15:26

3 Answers3

51

You can use ranges to find the position of elements. Quick jsfiddle here: https://jsfiddle.net/abrady0/ggr5mu7o/

var range = document.createRange();
range.setStart(parentElt, start);
range.setEnd(parentElt, end);
// These rects contain the client coordinates in top, left
var rects = range.getClientRects();

Edit: modified to print out the coordinates of the matched rect

aaron
  • 1,746
  • 1
  • 13
  • 24
  • This is useful for getting the character index of where the user clicks, but I think OP was looking for x,y coordinates in pixels. – tobek Jun 19 '15 at 20:44
  • 2
    my bad, I should have been more clear in my example: the client rects contain the client coordinates of where the click happened. this can easily be used to target a specific character or set of characters. – aaron Jun 23 '15 at 16:06
  • This is so awesome, why didn't I find it sooner? – Tomáš Zato Jun 13 '18 at 17:01
  • 1
    This is just what I was looking for. Only one remark, instead of `parentElt` in your code example it would be better to use something like `parentElt.childNodes[0]` because like is shown in your jsfiddle example [`range.setStart`](https://developer.mozilla.org/en-US/docs/Web/API/Range/setStart) and [`range.setEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd) take text node as argument instead of html element. – ands Aug 02 '18 at 00:44
  • @ands There's a condition check if it is a text node. I didn't find it must be a text node if end>1, either, until I found this check. Ha ha – Eric Aug 06 '18 at 11:39
  • 1
    thank you for this answer! i modified it to do a binary search to find the exact character clicked. also i added `style="white-space: pre-wrap"` to handle multiple spaces https://jsfiddle.net/bomelino/fmx5awy2/58/ – tino Sep 02 '20 at 11:17
  • the demo doesn't seem to work when there is a or any other element in the middle of the text. – Flafy Apr 26 '23 at 17:31
14

If you know the string position of the character in the text, you can wrap the character in a <span> or similar element with an ID and find the x,y coords of that element. Easiest way to do this is with jQuery or another library, see this answer for a cross-browser, no-library method.

Community
  • 1
  • 1
glomad
  • 5,539
  • 2
  • 24
  • 38
  • Too much processing in my case but nice solution. Updated question to reflect this. – Jamie Feb 28 '11 at 15:10
  • 1
    You don't need to wrap every character in a span, just the one whose coordinates you need. Once you get the position you can unwrap it. – glomad Feb 28 '11 at 15:16
  • In my case I was looking for position of lines with tolerance +-2 lines or so. I found that the span trick works quite efficiently if you are looking only for words succeeded with punctuation characters [.,!?-]. In English sentences you will encounter punctuation almost in every line of text (unless you have very tiny columns). As such looking for those words will give you position of almost any line of text. Regex for such words would be: /\S+[,.!?-]/gi – J. Wrong Mar 22 '16 at 22:22
  • 2
    Unfortunately, because of kerning, it's possible that the insertion of a `span` tag around a character will actually affect its coordinates. E.g. see this: https://jsfiddle.net/4p4uw94h/ – danfuzz Nov 17 '16 at 20:00
  • 1
    @danfuzz Very interesting. It also affects ligatures (ff, fi). Check this: [jsfiddle.net/x4zjcbft](http://jsfiddle.net/x4zjcbft) Any ideas? – glomad Dec 09 '16 at 22:06
  • 3
    @ithcy The second-most upvoted answer (ranges) seems to be the way to go. – danfuzz Jan 23 '17 at 20:17
4

Idea i get is - get all the text , pack required substring into span-s , replace innerHTML of element , and get position of span-s

SergeS
  • 11,533
  • 3
  • 29
  • 35
  • I agree it would work, although it's to much processing for what I nee d it for. Nice idea though. – Jamie Feb 28 '11 at 15:01