13

I'm looking to move the caret exactly four spaces ahead of its current position so that I can insert a tab properly. I've already got the HTML insertion at the caret's position working, but when I insert the HTML, the caret is left behind. I've spent the past hour or so looking at various ways to do this and I've tried plenty of them, but I can't get any of them to work for me. Here's the most recent method I've tried:

function moveCaret(input, distance) { if(input.setSelectionRange) { input.focus(); input.setSelectionRange(distance, distance); } else if(input.createTextRange) { var range = input.createTextRange(); range.collapse(true); range.moveEnd(distance); range.moveStart(distance); range.select(); } } 

It does absolutely nothing--doesn't move the caret, throw any errors or anything. This leaves me baffled. And yes, I know that the above method set (is supposed to) set the caret at a certain position from the beginning of the specified node (that is, input), but even that's not working. So, what exactly am I doing wrong, and how can I do it right?


Edit: Based on the links that o.v. provided, I've managed to cobble something together that's finally doing something: throwing an error. Yay! Here's the new code:

this.moveCaret = function(distance) { if(that.win.getSelection) { var range = that.win.getSelection().getRangeAt(0); range.setStart(range.startOffset + distance); } else if (that.win.document.selection) { var range = that.win.document.selection.createRange(); range.setStart(range.startOffset + distance); } } 

Now, this gives the error Uncaught Error: NOT_FOUND_ERR: DOM Exception 8. Any ideas why?

10
  • Did you try setting the element.selectionStart and element.selectionEnd? It should work cross-browser for input and textareas as far as I remember. Commented May 28, 2012 at 0:49
  • I was avoiding it because I believe it doesn't working in earlier versions of IE8. I'll take a look at it anyhow, though. Commented May 28, 2012 at 0:50
  • The only IE version I test with is IE9, best of luck though. :) Commented May 28, 2012 at 0:51
  • Yes, it would appear selectionStart doesn't work in earlier versions of IE, but thanks anyhow. =) Commented May 28, 2012 at 0:52
  • Possible duplicate: stackoverflow.com/questions/1181700/… This and this answer appear to have received a lot of recognition. Commented May 28, 2012 at 0:55

1 Answer 1

27

The code snippet you have is for text inputs and textareas, not contenteditable elements.

Provided that all your content is in a single text node and the selection is completely contained within it, the following will work in all major browsers, including IE 6.

Demo: http://jsfiddle.net/9sdrZ/

Code:

function moveCaret(win, charCount) { var sel, range; if (win.getSelection) { // IE9+ and other browsers sel = win.getSelection(); if (sel.rangeCount > 0) { var textNode = sel.focusNode; var newOffset = sel.focusOffset + charCount; sel.collapse(textNode, Math.min(textNode.length, newOffset)); } } else if ( (sel = win.document.selection) ) { // IE <= 8 if (sel.type != "Control") { range = sel.createRange(); range.move("character", charCount); range.select(); } } } 
Sign up to request clarification or add additional context in comments.

11 Comments

+1 because it's a really useful snippet of code. Just a question: why do you access the global via the win argument?
@AndreaParodi: To allow use with an iframe. In this case it's the window host object we're interested in rather than its other role as global object. For an iframe, you'd call moveCaret(iframeElement.contentWindow, charCount).
However if you need to move to the previous text node, this fails as sel.focusNode == 0
@danm07: What kind of explanation do you want? The Range and Selection methods are standard and well documented. The code is short and simple. Also, if you want to annotate it with comments, it's possible for you to do so. The only comments I feel might be useful would be labels for the two main code branches (IE 9+ and other browsers, IE 6 - 8).
@danm07: I'm guilty of that in hundreds of answers. I don't think it's universal practice to add a running commentary to all code on SO; I'm not sure whether I think it should be. Does more hand-holding encourage or discourage people from looking up the browser APIs I've used here? I don't know. Anyway, I've added a little detail in this answer.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.