Javascript - Caret Position
The caret is where text goes when typing into a text block.
Most programming languages provide proper support for this critical feature -
however, this appears to have been intentionally omitted from javascript
before it was added in HTML5.
(This opinion is supported by the large number of search hits where developers are looking
for a method to read the current position.)
I have 2 applications where I need this info
- When typing in a number field, I want the caret to remain in a logical place
- In a plotting support application, I need to determine which line of text the caret is on.
To be clear,
- The mouse moves the cursor
- Text fields use a caret to indicate where text is inserted
- Left clicking the mouse typically sets the caret location
Using the HTML5 syntax,
the text field testcases below work with Chrome 47 (Windows XP) and all 4 test browsers on Windows 10 -
Chrome, Firefox, Edge, IE.
The number field test only works with Firefox on Windows 10.
(Edge and IE on Windows 10 have no idea what a number field is and just treat
it as a regular text field.)
The one special case was IE8 (which is obsolete) and the two
special testcases work with that.
HTML5 solution
- setSelectionRange()
| Cross Browser Support
| Reading the caret position
| Notes
HTML5 solution
Among other properties, the following are part of the HTML5 recommendation
for
<input type=text> and <textarea> components.
Similar text is repeated on mozilla.org at
HTMLInputElement,
HTMLTextAreaElement, and
setSelectionRange().
|
selectionStart | unsigned long
| Returns / Sets the beginning index of the selected text.
When nothing is selected, this returns the position of the text input cursor (caret) inside of the <input> element.
|
---|
selectionEnd | unsigned long
| Returns / Sets the end index of the selected text.
When there's no selection, this returns the offset of the character immediately following the current text input cursor position.
|
---|
Both values are zero-based (the first position is zero).
When both values are the same, nothing is selected and they indicate the caret position.
setSelectionRange()
The following quotes are from
mozilla.org - HTMLInputElement.setSelectionRange().
The HTMLInputElement.setSelectionRange() method sets the start and end positions
of the current text selection in an <input> or <textarea> element.
This method updates the HTMLInputElement.selectionStart, selectionEnd, and selectionDirection
properties in one call.
|
Syntax
element.setSelectionRange(selectionStart, selectionEnd [, selectionDirection]);
|
(According to
this reference,
setSelectionRange failed with number fields in Chrome 33, but
was fixed in 2013 (This link is dead and without a backup as of 09-10-2019).
My testing is with Chrome 49 and later where it fails.)
The following sets the carets to position 3.
They both fail in IE8 - no surprise, it does not support HTML5.
The text field works with Chrome 49 (XP) and my 4 test browsers on Windows 10.
However,
- IE and Edge on Windows 10 have no idea what a number field is -
you can type any character you want.
- Firefox on Windows 10 has a pretty crappy number field
- You can type anything you want, but the field outline turns red for bad values
- The up/down arrows are way too small to use
but the code below does set the caret.
- With Chrome 49 (XP), and with the Windows 10 version,
the number field works ok,
but trying to set the caret FAILS -
the following is copied from the debug window.
Uncaught InvalidStateError: Failed to execute 'setSelectionRange' on 'HTMLInputElement':
The input element's type ('number') does not support selection.
|
It isn't clearly documented, but ctrl.focus() is required before some of these commands work,
but not for others.
Cross Browser Support
For my current application - If it works in Chrome, the code is fine.
However, at some point I might care about cross browser support.
The following approach (edited for clarity) is from
Using setSelectionRange and createTextRange
function setSelectionRange(input, selectionStart, selectionEnd) {
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
}
else if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd ('character', selectionEnd );
range.moveStart('character', selectionStart);
range.select();
}
}
function setCaretToPos (input, pos) {
setSelectionRange(input, pos, pos);
}
|
The first works in all my test browsers, including IE8 - it sets the caret to position 3.
As expected, the second (number) test still fails in both versions of Chrome,
but works in Firefox on Windows 10.
Reading the caret position
Reading the caret position from a multi-line textarea component
is a bit trickier because I don't care about the number of characters from the beginning - instead I want to know the row and column!
function getRowColumn (input) {
var x = input.selectionStart;
var v = input.value.substr(0, input.selectionStart).split("\n"); // make an array
row.value = v.length;
column.value = v[v.length-1].length;
}
|
I tried many methods to make this work with IE8.
The main problem is that I never found useful documentation on createTextRange.
The next big problem was that the technique used in some examples ignored the fact
that IE treats
CRLF
as a single character in some cases, and as 2 characters in others.
As a result, the returned values would not sync up.
After several hours, I found
one that worked!
(attached to the IE test button above).
Notes
The IE8 technique to work with a caret uses the range functions
(which are not documented via w3schools .. or anywhere else that I could find).
Of course, this is all confused by the
HTML5 range control
which provides a slider
(in those browsers that support it).
The following is the total caret related help provided via
w3schools
Window Object Methods
getSelection() Returns a Selection object representing the range of text selected by the user
|
One of the common irritations is that the browser spellchecks data files in textarea components -
just
set the global spellcheck attribute to false.
<textarea id=datafile style="white-space: pre; resize:none;" spellcheck="false"></textarea>
|
In order to highlight more than one discontinuous line of text,
and to keep those highlights visible when focus is removed from the control,
the text can not be placed in a textarea control.
This code
works with a div
- it is far more complicated than the code above.
Author:
Robert Clemenzi