Mar 8, 2010

Calculating the cursor position in textarea with JavaScript

I've been spending some time writing tag support in Checkvist, and decided to share a bit of related JavaScript code.

The idea is to allow adding tags with smart syntax: when you write "Call Bob regarding new furniture tomorrow #home" Checkvist will create a task "Call Bob regarding new furniture" with due tomorrow and with tag #home.

The additional nicety could be the tag completion after the '#' character. But here comes a problem - how to show the completion popup near the cursor position in the textarea? The typical approach used in del.icio.us or GMail is to show the popup under the text field, but it doesn't look good when your cursor is far from the popup, in the beginning of a large text area.

After some googling I didn't find an existing solution. The only helpful code was detection of the text cursor position relatively to the beginning of the string.

To convert this position into pixel coordinates, one has to find out how the text is organized within the textarea, where linebreaks are, how many lines are there in the text, and so on. This, in turn, depends on the font metrics of the text and requires answer for the question "What is the length of the given string if it is placed into this textarea?".

The answer for the last question can be obtained if you create a div with the same font metrics as the original textfield, give it absolute positioning, put a string into it, and take its width. Using such a function, you can model text wrapping in the textarea, and find out the actual X,Y coordinates of the cursor in the textarea.

You can find my implementation of this approach on GitHub (BTW, a really great place to share open-source code). This library was tested with FF3, Chrome, IE, Opera, Safari. There may be some glitches, but they are rare and I'm pretty comfortable with the results so far. There is no need for Prototype, jQuery or other Javascript libraries to use the code.

I hope this code will be useful to someone else, and if so, please drop me a line :)
Post a Comment