Set Cursor Position In Content-editable Div
Solution 1:
Here's a much simpler approach. There are a few things to note:
keypress
is the only key event in which you can reliably detect which character has been typed.keyup
andkeydown
won't do.- The code handles the insertion of parentheses/braces manually by preventing the default action of the
keypress
event. - The selection/caret stuff is simple when using DOM methods.
- This won't work in IE <= 8, which have different range and selection APIs. If you need support for those browsers, I'd suggest using my own Rangy library. It is possible without it but I really don't want to write the extra code.
Demo:
Code:
var editableEl = document.getElementById("editable");
editableEl.addEventListener("keypress", function(e) {
var charTyped = String.fromCharCode(e.which);
if (charTyped == "{" || charTyped == "(") {
// Handle this case ourselves
e.preventDefault();
var sel = window.getSelection();
if (sel.rangeCount > 0) {
// First, delete the existing selectionvarrange = sel.getRangeAt(0);
range.deleteContents();
// Insert a text node at the caret containing the braces/parensvar text = (charTyped == "{") ? "{}" : "()";
var textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move the selection to the middle of the inserted text noderange.setStart(textNode, 1);
range.setEnd(textNode, 1);
sel.removeAllRanges();
sel.addRange(range);
}
}
}, false);
Solution 2:
To accompish the goal stated in your summary, try altering the node value at the current cursor position. Since your code is attached to a keyup
event, you can be assured that the range is already collapsed and on a text node (all of that happens on key down, which would have already fired).
function insertChar(char) {
var range = window.getSelection().getRangeAt(0);
if (range.startContainer.nodeType === Node.TEXT_NODE) {
range.startContainer.insertData(range.startOffset, char);
}
}
function handleKeyUp(e) {
e = e || window.event;
varchar, keyCode;
keyCode = e.keyCode;
char =
(e.shiftKey ? {
"222": '"',
"57": ')',
"219": '}'
} : {
"219": "]"
})[keyCode] || null;
if (char) {
insertChar(char);
}
}
document.getElementById("editable").onkeyup = handleKeyUp;
Also, I see that you were using innerHTML
to set the new value (in Element.prototype.setText
). This sounds alarming to me! innerHTML
totally nukes whatever contents were previously in the container. Since the cursor is bound to a particular element, and those elements just got nuked, what is the browser supposed to do? Try to avoid using this if you care at all about where your cursor ends up afterwards.
As for the highlightText
issue, it's hard to say why it is broken. Your fiddle does not show it being used anywhere, and I would need to see its usage to further diagnose it. However, I have an idea about what might be going wrong:
I think you should take a close look at getCaretPosition
. You are treating this as though it returns the cursor position, but that isn't what it does. Remember, to a browser, your cursor position is always a range. It always has a beginning and an end. Sometimes, when the range is collapsed, the beginning and the end are the same point. However, the idea that you could get a cursor position and treat it as a single point is a dangerous oversimplification.
getCaretPosition
has another issue. For your editable div
, it does this:
- Selects all of the text in a new range
- Resets the new range's end position to equal the cursor's end position (so all text up to the cursor's end position is selected).
- Calls
toString()
and returns the length of the resulting string.
As you have noted, some elements (such as <br />
) affect the results of toString()
. Some elements (such as <span></span>
) do not. In order to get this calculation right, you'll need to nudge it for some element types and not for others. This is going to be messy and complicated. If you want a number that you can feed into highlightText
and have it work as expected, your current getCaretPosition
is unlikely to be helpful.
Instead, I think you should try working directly with the cursor's start and end points as two separate locations, and update highlightText
accordingly. Discard the current getCaretPosition
and use the browser's native concepts of range.startContainer
, range.startOffset
, range.endContainer
, and range.endOffset
directly.
Post a Comment for "Set Cursor Position In Content-editable Div"