Geeks With Blogs

@scottvanvliet
  • scottvanvliet Tonight is my first show in 10 years, guest bass for The Toys! Learned 30+ songs in 4 weeks. Might get ugly but going to be fun! Thx @TKilp about 423 days ago

Scott Van Vliet Once a Developer, Always a Developer

I’d recently searched around for some good-quality JavaScript snippets to determine the cursor position within an HTML TextArea, but haven’t had any luck.  So, like any fellow geek would do, I came up with my own solution.

 

The DOM in IE does not provide information regarding relative position in terms of characters; however, it does provide bounding and offset values for browser-rendered controls.  Thus, I used these values to determine the relative bounds of a character. Then, using the JavaScript TextRange, I created a mechanism for working with such measures to calculate the Line and Column position for fixed-width fonts within a given TextArea.

 

First, the relative bounds for the TextArea must be calculated based upon the size of the fixed-width font used.  To do this, the original value of the TextArea must be stored in a local JavaScript variable and clear the value.  Then, a TextRange is created to determine the Top and Left bounds of the TextArea.

 

var storedValue = textBox.value;

 

textBox.value = "";

textBox.select();

 

var caretPos = document.selection.createRange();

textBox.__boundingTop = caretPos.boundingTop;

textBox.__boundingLeft = caretPos.boundingLeft;

 

Next, in order to capture the bounding width of a single fixed-width character, the value of the TextArea is set to a single character, selected, and another TextRange is created.

 

textBox.value = " ";

textBox.select();

 

caretPos = document.selection.createRange();

textBox.__boundingWidth = caretPos.boundingWidth;

textBox.__boundingHeight = caretPos.boundingHeight;

 

textBox.value = storedValue;

 

The values obtained from the initialization will persist in the instance of the TextArea, textBox.  Also, in order for this initialization to occur at least once, it must be registered with the onLoad event.

 

<body onload="initPosition(document.forms[0].txtLayoutViewer)">

 

Next, the TextArea must be configured to capture the cursor position.  Mouse and keyboard activity will be captured as follows.

 

 <textarea name="txtLayoutViewer"

   onmouseup="updatePosition(this)"

   onmousedown="updatePosition(this)"

   onkeyup="updatePosition(this)"

   onkeydown="updatePosition(this)"

   onfocus="updatePosition(this)"

   rows="15"

   cols="75"></textarea>

 

When the updatePosition method is called, another TextRange is created to capture the cursor selection.  Then, using the values calculated during the initialization and those in the TextRange, the Line and Column values are calculated as follows.

 

var caretPos = document.selection.createRange();

 

var boundingTop = (caretPos.offsetTop + textBox.scrollTop) - textBox.__boundingTop;

var boundingLeft = (caretPos.offsetLeft + textBox.scrollLeft) - textBox.__boundingLeft; 

 

textBox.__Line = (boundingTop / textBox.__boundingHeight) + 1;

textBox.__Column = (boundingLeft / textBox.__boundingWidth) + 1;

 

As with the bounds captured during initialization, the Line and Column values persist in the instance of the TextArea.  They can then be used be other elements throughout the DOM. 

 

Below is the complete code listing.

 

<html>

<head>

    <script language="JavaScript" type="text/javascript">

    <!--

        function initPosition(textBox) {

            var storedValue = textBox.value;

            textBox.value = "";

            textBox.select();

 

            var caretPos = document.selection.createRange();

            textBox.__boundingTop = caretPos.boundingTop;

            textBox.__boundingLeft = caretPos.boundingLeft;

                    

            textBox.value = " ";

            textBox.select();

 

            caretPos = document.selection.createRange();

            textBox.__boundingWidth = caretPos.boundingWidth;

            textBox.__boundingHeight = caretPos.boundingHeight;

 

            textBox.value = storedValue;

        }

 

        function storePosition(textBox) {

            var caretPos = document.selection.createRange();

 

            var boundingTop = (caretPos.offsetTop + textBox.scrollTop) - textBox.__boundingTop;

            var boundingLeft = (caretPos.offsetLeft + textBox.scrollLeft) - textBox.__boundingLeft;

 

            textBox.__Line = (boundingTop / textBox.__boundingHeight) + 1;

            textBox.__Column = (boundingLeft / textBox.__boundingWidth) + 1;

        } 

 

        function updatePosition(textBox) {

            storePosition(textBox);

            document.forms[0].txtLine.value = textBox.__Line;

            document.forms[0].txtColumn.value = textBox.__Column;

        }

    //-->

    </script>

    <style type="text/css">

        body, td, tg, input, select {

            font-family: Verdana;

            font-size: 10px;

        }

    </style>

</head>

<body onload="initPosition(document.forms[0].txtLayoutViewer)">

    <form>

        <table cellspacing="0" cellpadding="3">

            <tr>

                <td colspan="3">

                    Change Font Size

                    <select onchange="this.form.txtLayoutViewer.style.fontSize = this.options[this.selectedIndex].value; initPosition(this.form.txtLayoutViewer);">

                        <option value="10">10px</option>

                        <option value="12">12px</option>

                        <option value="14">14px</option>

                        <option value="16">16px</option>

                        <option value="18">18px</option>

                        <option value="20">20px</option>

                        <option value="24">24px</option>

                        <option value="36">36px</option>

                    </select>

                </td>

            </tr>

            <tr>

                <td colspan="3">

                    <textarea name="txtLayoutViewer"

                        onmouseup="updatePosition(this)"

                        onmousedown="updatePosition(this)"

                        onkeyup="updatePosition(this)"

                        onkeydown="updatePosition(this)"

                        onfocus="updatePosition(this)"

                        rows="15"

                        cols="75">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec ornare aliquam quam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque et quam in dui consequat tempor. Etiam lorem lectus, sollicitudin laoreet, tincidunt nec, pharetra in, magna. Mauris accumsan velit et augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</textarea></td>

            </tr>

            <tr>

                <td width="70%">

                    &nbsp;</td>

                <td width="10%">

                    Line <input type="text" name="txtLine" style="width: 25px" readonly></td>

                <td width="20%">

                    Column <input type="text" name="txtColumn" style="width: 25px" readonly></td>

            </tr>

        </table>

    </form>

</body>

</html>

 

Please note that this has only been tested on IE 6.  As always, I welcome your feedback and constructive criticism.

UPDATE 3/29/3005:

A friend of mine filled me in on a limitation to this approach.  If the TextArea extends beyond the screen, thus requiring scrolling, the calculated values will be incorrect due to the fact that the bounds used in the calculation change when the window has scrolled.  Moreover, if the text you are working with requires that you scroll beyond the viewable portion of the TextArea, the calculated values will reverse.  In order to fix this, the scolling index of the screen and/or TextArea must be obtained to offset the calcuation.  Thus, if you plan on using this approach ensure that the requirements are such that the TextArea does not require scrolling.  Otherwise, if you feel so inclined, email with your thoughts on obtaining the offset value required when scrolling.  Thanks!

UPDATE 4/12/2005:

Thanks to Mr. Noisy, the previous bug listed (TextArea scrolling) has been fixed, and now accounts for the scrollTop and scrollLeft properties of the TextArea.  I've updated the code in this post to reflect the changes.

Posted on Thursday, March 24, 2005 10:31 PM Javascript , Technology | Back to top


Comments on this post: TextArea Cursor Position with JavaScript

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
What version of IE are you using? I an using IE6 SP2, and I cannot reproduce the bug you are describing.
Left by Scott Van Vliet on Mar 25, 2005 1:50 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Neato, thanks for sharing! :)
Left by Erik Porter on Mar 25, 2005 2:34 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
aflorin,
<br>
<br>Thanks for your message. Indeed the link you posted was good, the script therein does not provide the caret position by Line Number and Column; whereas, the solution I provided does -which is why I posted it ;)
Left by Scott Van Vliet on Apr 21, 2005 8:35 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
You do realize that you can just check the selectionStart to find the caret position much like you would for the selected text?

Just check if the length of the selected text is less than 1, in which case nothing is selected and the start and end selection will simply return the position the caret is in.

Works fine for me in FireFox using this method. I use it for inserting opening and closing tags in my editor at the caret position.
Left by Chesso on Sep 03, 2006 2:50 AM

# Try this, much simpler
Requesting Gravatar...
There's a much simpler way of getting selection start and end offsets in IE than using pixels and what not:
-----------------------------------------------------

// get current selection
var range = document.selection.createRange();

// make sure it's the textarea's selection
if (range.parentElement().id == 'my_textarea_id')
{
// create a selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(my_textarea_obj);

// calculate selection start point by moving
// beginning of range_all to beginning of range
for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++)
{
range_all.moveStart('character', 1);
}

// calculate selection end point by moving
// end of range_all to end of range
for (var sel_end = my_textarea_obj.innerHTML.length; range_all.compareEndPoints('EndToEnd', range) > 0; sel_end --)
{
range_all.moveEnd('character', -1);
}

// sel_start and sel_end hold now the offsets!
}
Left by Strifer on Sep 04, 2006 4:45 PM

# Re: Try this
Requesting Gravatar...
Edit:
------
use: my_textarea_obj.value.length
instead of: my_textarea_obj.innerHTML.length
Left by Strifer on Sep 04, 2006 6:03 PM

# Try this
Requesting Gravatar...
Argh IE will drive me nuts some day! I had to do one more adjustment. Anyway here's now the complete code for getting the textarea selection offsets cross-browser as well as the selected text:
----------------------------------------------------

// get selection in firefox, opera, ...
if (typeof(textarea.selectionStart) == 'number')
{
// get start and end points of selected text
textarea.sel_start = textarea.selectionStart;
textarea.sel_end = textarea.selectionEnd;

// get selected and surrounding text
textarea.sel_text = bbt_textarea.value.substring(textarea.sel_start, textarea.sel_end);
textarea.sel_text_pre = textarea.value.substring(0, textarea.sel_start);
textarea.sel_text_post = textarea.value.substring(textarea.sel_end, textarea.value.length);
}
// get selection in IE
else if (document.selection)
{
// make sure it's the textarea's selection
var range = document.selection.createRange();
if (range.parentElement().id == 'textarea_id')
{
// create a selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(textarea);

// calculate selection start point by moving beginning of range_all to beginning of range
for (var sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start ++)
range_all.moveStart('character', 1);

// get number of line breaks from textarea start to selection start and add them to sel_start
for (var i = 0; i <= sel_start; i ++)
{
if (textarea.value.charAt(i) == '\n')
sel_start ++;
}
textarea.sel_start = sel_start;

// create a selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(textarea);

// calculate selection end point by moving beginning of range_all to end of range
for (var sel_end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; sel_end ++)
range_all.moveStart('character', 1);

// get number of line breaks from textarea start to selection end and add them to sel_end
for (var i = 0; i <= sel_end; i ++)
{
if (textarea.value.charAt(i) == '\n')
sel_end ++;
}
textarea.sel_end = sel_end;

// get selected and surrounding text
textarea.sel_text = range.text;
textarea.sel_text_pre = textarea.value.substring(0, textarea.sel_start);
textarea.sel_text_post = textarea.value.substring(textarea.sel_end, textarea.value.length);
}
}
// this browser doesn't support javascript selections
else
{
...
}
----------------------------------------------------

I used the textarea object to store the selections in it:
selection start offset: textarea.sel_start
selection end offset: textarea.sel_end
selected text: textarea.sel_text
text left of selection: textarea.sel_text_pre
text right of selection: textarea.sel_text_post
Left by Strifer on Sep 06, 2006 10:50 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Your solution is great. It seems like this would work on any HTML element.

Here is my textarea solution: http://linebyline.blogspot.com/2006/11/textarea-cursor-position-in-internet.html
Left by Jake on Nov 10, 2006 9:21 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Nice article. With some fix now it working great! ;)
Left by Steve on May 06, 2008 2:14 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
any of above solutions are not clear. plz can anybody can help to set cursor to the specific position together with cross browser capability.
Left by nayana adassuriya on Oct 16, 2008 6:03 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
I need to deal with Unicode.
How about fire keydownEvent. and test the special character background??
Left by ghg on Jan 15, 2009 1:52 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
very Good.
Thanks a lots!!!
Left by ghg on Jan 15, 2009 2:12 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
This may be helpful.

http://websitenotebook.blogspot.com/2008/12/adding-tab-functionlity-in-ie-and.html
Left by Teri Radichel on Mar 16, 2009 9:31 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Now I come from a long line of women who think that a man is easier on the eyes in a uniform. ,
Left by Alex19 on Oct 22, 2009 10:37 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
I found out that IE only count 1 character for the Carriage Return / Line Feed, thus putting off the caret position since IE count 1 for the 2 characters are stored in the innerText of the textarea. You will have to create a scratch text and delete 1 of the CR or LF from the innerText.

data = myTextarea.innerText.replace(/\r/g,'');

This is for Inut and output to the TextRange objects.

I am working on an on-line example that uses this logic (which has all the source code) http://collinssoftware.com/Graphic.htm to double-click, extract the line of text, popup a form to edit, then replace.

The count problem is only in IE, firefox is OK.

Clif Collins
c.collins@collinssoftware.com

Left by Clif on Dec 29, 2009 10:59 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Good ! I like this!
Left by zihou on Mar 17, 2010 1:43 PM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
Here is my solution, it moves a bookmark from the document.selection textrange to an element textrange. It correctly calculates the start and end positions regardless of where the element is in the page.

function getSelectionRange(oElm)
{
var $r = { text: "", start: 0, end: 0, length: 0 };
if (oElm.setSelectionRange)
{ // W3C/Gecko
$r.start= oElm.selectionStart;
$r.end = oElm.selectionEnd;
$r.text = ($r.start != $r.end) ? oElm.value.substring($r.start, $r.end): "";
}
else if (document.selection)
{ // IE
if (oElm.tagName && oElm.tagName === "TEXTAREA")
{
var $oS = document.selection.createRange().duplicate();
var $oR = oElm.createTextRange();
var $sB = $oS.getBookmark();
$oR.moveToBookmark($sB);
}
else
var $oR = document.selection.createRange().duplicate();
$r.text = $oR.text;
for (; $oR.moveStart("character", -1) !== 0; $r.start++);
$r.end = $r.text.length + $r.start;
}
$r.length = $r.text.length;
return $r;
}
Left by Jerry on May 12, 2010 9:49 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
I have surfed for a while looking for script to will check for textarea row. Your code works really well, thank u.
Left by adrian on Aug 10, 2011 7:36 AM

# re: TextArea Cursor Position with JavaScript
Requesting Gravatar...
hii i am using IE7 and i am integrating it in asp.net using visual studio plz help with me with changes
Left by surendra on Jun 16, 2014 3:02 AM

Your comment:
 (will show your gravatar)
 


Copyright © svanvliet | Powered by: GeeksWithBlogs.net | Join free