Caret Position Woes

Anyone that has tried to create a rich in-browser HTML text editor has had to deal with Internet Explorer and its caret positioning issues (For those wondering that’s the blinking line that shows where you’re typing. It’s also called a cursor but people usually reserve that to refer to the mouse). It isn’t too difficult if you use BODY, BUTTON, TEXTAREA, or an INPUT element having text type (TYPE=”text”). In that case it’s just a matter of using createTextRange() and finding the text position. For anyone trying to figure out how to do that, here is some example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<html>
 
 <body style="font-family: tahoma; font-size: 8pt;">
 
  <script language="JavaScript">
 
   /*
   **  Returns the caret (cursor) position of the specified text field.
   **  Return value range is 0-oField.length.
   */
   function doGetCaretPosition (oField) {
 
     // Initialize
     var iCaretPos = 0;
 
     // IE Support
     if (document.selection) { 
 
       // Set focus on the element
       oField.focus ();
 
       // To get cursor position, get empty selection range
       var oSel = document.selection.createRange ();
 
       // Move selection start to 0 position
       oSel.moveStart ('character', -oField.value.length);
 
       // The caret position is selection length
       iCaretPos = oSel.text.length;
     }
 
     // Firefox support
     else if (oField.selectionStart || oField.selectionStart == '0')
       iCaretPos = oField.selectionStart;
 
     // Return results
     return (iCaretPos);
   }
 
 
   /*
   **  Sets the caret (cursor) position of the specified text field.
   **  Valid positions are 0-oField.length.
   */
   function doSetCaretPosition (oField, iCaretPos) {
 
     // IE Support
     if (document.selection) { 
 
       // Set focus on the element
       oField.focus ();
 
       // Create empty selection range
       var oSel = document.selection.createRange ();
 
       // Move selection start and end to 0 position
       oSel.moveStart ('character', -oField.value.length);
 
       // Move selection start and end to desired position
       oSel.moveStart ('character', iCaretPos);
       oSel.moveEnd ('character', 0);
       oSel.select ();
     }
 
     // Firefox support
     else if (oField.selectionStart || oField.selectionStart == '0') {
       oField.selectionStart = iCaretPos;
       oField.selectionEnd = iCaretPos;
       oField.focus ();
     }
   }
 
  </script>
 
  <form name="blah">
 
   Text Field: <input type="text" name="nameEdit" value="">
   <input type="button" value="Get Caret" onClick="document.getElementById('where').value=doGetCaretPosition (document.forms[0].elements[0]);">
<input id="where">
   <hr size=1 noshade>
   New Position: <input type="text" name="newPosValue" value="">
   <input type="button" value="Set Caret" onClick="doSetCaretPosition (document.blah.nameEdit, parseInt (document.blah.newPosValue.value));">
 
  </form>
 
 </body>
 
</html>

Need something more powerful? Here’s a nifty little JQuery plugin.

If you’ve tried doing that with a contentEditable div, you’ll have realized that it’s much more difficult. You see friendly Internet Explorer doesn’t support createTextRange() for contentEditable divs, and since they are better for rich html, this creates a problem.

Solution: be tricky too. Instead of creating the text range on the node, create it on the BODY and then move it to the node! I’m a dojo man (take a look at the Dojo Toolkit here), so in dojo you type:

1
2
3
4
5
6
7
8
9
var range = dojo.doc.selection.createRange();
var rs = dojo.body().createTextRange();
rs.moveToElementText(yourContentEditableDivNode);
//Now duplicate the range so we can use one to get the end point
var re = rs.duplicate();
rs.moveToBookmark(range.getBookmark());
re.setEndPoint('EndToStart', rs);
//Now re is only the text up until the caret
console.warn("The text before the cursor is: ",re.text," Its position is: ",re.text.length);

Dojo has a rich text editor, and it uses a class dijit._editor.range which is a singleton and has quite a bit of useful text for getting ranges in IE. If you haven’t used dojo before for Javascript development I’d recommend it, it’s an incredible open source library. Click here to see an example implementation (you should really look at this it’s fantastic).

Google has some code that should give IE w3c selections. It’s here.

Leave a Reply


− five = 3