4

How to get position (left, top) of every characters in some node? I know just one method: wrap every character in it's own tag and then we can get it's coordinate. But it's bit slowly

anton_byrna
  • 2,477
  • 1
  • 18
  • 31
  • I fear you don't have much choice except the method you described. To know the position of every character you must know the exact size of each letter and that's pretty much impossible task as each font has different size for each character.. – Shadow The GPT Wizard Nov 30 '10 at 09:00
  • Possible duplicate: http://stackoverflow.com/questions/5143534/get-the-position-of-text-within-an-element – Anderson Green Jun 03 '13 at 23:12

2 Answers2

8

I've found answer on my question — Range interface can present all information that I need (More info — http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html).

anton_byrna
  • 2,477
  • 1
  • 18
  • 31
1

You could cache the widths of characters that you already have seen.

function findOffset(index) {
  // Set some variables early on
  var sizes          = {},
      text           = "The text you are getting an offset from.",
      lineHeight     = 16,  // You can get this dynamically if you want
      containerWidth = 500, // Same with this one
      leftOffset     = 0,
      topOffset      = 0,
      i              = 0,
      cur;

  // Loop through and count up the sizes of each character until this one
  for (; (i < text.length) && (i <= index); i++) {

    // Set the current character
    cur = text.charAt(i);

    // Check to see if we have a size
    if ( typeof size[cur] == "undefined" ) {
      // If not: Wrap it in a span (You seemed to already know how to do this)
      // then cache the result
      size[cur] = findWidthByTemporarilyWrappingInASpan(text, i);
    }

    // If it's greater than a line can hold, we'll wrap
    if ( (sizes[cur] + leftOffset) > containerWidth ) {

      // Reset the left offset
      leftOffset = 0;

      // Increment the top offset
      topOffset += lineHeight;
    }
    // Otherwise, increment from the left
    else {
      leftOffset += sizes[cur];
    }
  }

  // return an object with the coordinates
  return {
    leftOffset: leftOffset,
    topOffset : topOffset
  };
}

Then you can even memoize each index that you go grab and start from a close by one the next time you call this function. This means you stay off the dom except for usually not too much more than ~50 (alphanumeric + punctuation, etc) times, rather than for each character.

This would certainly work for monospaced fonts, but I think there is some merit to it for other types. You'd just have to do the wrapping research for different browsers.

Also, note that this assumes left-justification, etc. It's more of an idea than an actual code solution.

Alex Sexton
  • 10,401
  • 2
  • 29
  • 41
  • Good idea, need to take into account the font family and font size (probably can be found with jQuery?) and add that to the cache key e.g. typical key will be "G_Tahoma_14" and another one "G_Tahoma_12". :) – Shadow The GPT Wizard Nov 30 '10 at 09:14
  • 1
    As I mention using Range object solve my problem and it much faster than wrapping every symbol in span node. – anton_byrna Dec 20 '12 at 11:30