Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set caret/cursor position in a contenteditable div between two divs

Consider the following contenteditable div.

<div contenteditable="true">
<div>bold text</div><div>bold text</div>
</div>

If I position the cursor between the two divs and start typing the text comes out bold instead of inserting a new text node between the two divs. The same happens if you hit home and try to type something in front of the first div. It becomes part of the first div.

If I inspect the startContainer of the range that's returned from the selection, I get the content for one of the div's instead of an empty text node as I would expect.

$( '#EDITABLE' ).focus();

var selection = window.getSelection();

var range = document.createRange();

var div = $('#div2').get(0);

range.setStartBefore(div);
range.collapse(true);

selection.removeAllRanges();
selection.addRange(range);

// cursor should now be between div1 and div2

range = window.getSelection().getRangeAt(0);

console.log("range object returned is: ", range);

// type something into the content editable div. why is it coming
// up bold since the cursor should be between the divs? it's 
// positioning the cursor at the end of the previous div. I want to
// target the spot between the divs. how?
div.content {
  display: inline-block;
  font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div contenteditable="true" id="EDITABLE">
  <div class="content" id="div1">Content for div1</div>
  <div class="content" id="div2">content for div 2</div>
</div>

Original jsfiddle: http://jsfiddle.net/9ZZpX/3/

The question is why does this happen? How can I select the spot between the divs so that when I type something it does not bold? (Obviously I can add a space and that works around the problem but that's quite ugly.)

You can see this working correctly at Facebook if you enter an @mention in a status update box and press HOME. If you type the text will not get highlighted.

The only thing I've been able to think of is intercepting the keypress and inserting a text node programmatically but that seems ugly.

I searched like crazy and can't find any references that document how this is really supposed to work. There is obviously something that I do not understand and the documentation is really lacking in this area.

(What I want to be able to do as well is detect when the cursor is about to enter one of these divs and jump over it. If the two divs are right next to each other, the cursor jumps into one of the divs and it mucks up the works.)

More info on what I'm trying to do: http://a-software-guy.com/2012/12/the-horrors-of-cursor-positioning-in-contenteditable-divs/

like image 576
Yermo Lamers Avatar asked Dec 31 '12 06:12

Yermo Lamers


People also ask

How to set the caret cursor position in a contenteditable element div)?

In order to set caret cursor position in content editable elements like div tag is carried over by JavaScript Range interface. The range is created using document. createRange() method.

How do I change cursor position?

SetCursorPosition(Int32, Int32) Method is used to set the position of cursor. Basically, it specifies where the next write operation will begin in the console window.

How do you move the cursor to the end of Contenteditable entity?

createTextRange();//Create a range (a range is a like the selection but invisible) range. moveToElementText(contentEditableElement);//Select the entire contents of the element with the range range. collapse(false);//collapse the range to the end point.


2 Answers

Browsers are inconsistent on this. Firefox will let you position the caret in more positions than most browsers but WebKit and IE both have definite ideas about valid caret positions and will amend a range you add to the selection to conform to the nearest valid position. This does make sense: having different document positions and hence behaviours for the same visual caret location is confusing for the user. However, this comes at the cost of inflexibility for the developer.

This is not documented anywhere. The current Selection spec says nothing about it, principally because no spec existed when browsers implemented their selection APIs and there is no uniform behaviour for the current spec to document.

One option would be to intercept the keypress event as you suggest, although this will not help when the user pastes in content using the edit or context menus. Another would be to keep track of the selection using mouse and key events, create elements with, say, a zero-width space character for the caret to be placed in and place the caret in one those elements when necessary. As you say, ugly.

like image 165
Tim Down Avatar answered Oct 12 '22 23:10

Tim Down


My answer will be just an addition to Tim's one, which is comprehensive.

AFAIK Facebook doesn't use content editable. Status box is made of a simple textarea and div layer underneath it on which they render blue rects for nicks.

Although, even if they did, that would be a different case, because nick would be an inline element and luckily with inline elements situation is simpler :).

Regarding positioning caret at inaccessible places - at CKEditor we had the same problem. There are many places where user can't move caret. We decided to solve this issue with a plugin called Magic-line. As you can see in the demo we bypassed the problem with selection completely and I think that this is the best way to solve this issue. It's very usable and in CKEditor 4.0.1 it will be (and already is on master) also fully accessible by keystrokes.

like image 21
Reinmar Avatar answered Oct 12 '22 22:10

Reinmar