摘要:
我正在努力实现用户在内容可编辑(
,第二[
或div
中输入)
或]
的效果是自动插入的,插入符号位于两者之间,即(
和)
之间。
在--
的右侧输入,并查看第一行中的工作方式,而在第二行中不起作用。
我的努力:
我使用此代码(Tim Down)来突出显示文本的某些部分并设置光标位置。前者有效,但后者没有:(
function getTextNodesIn(node) { // helper
var textNodes = [];
if (node.nodeType == 3) {
textNodes.push(node);
} else {
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i) {
textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
}
}
return textNodes;
}
function highlightText(el, start, end) { // main
if (el.tagName === "DIV") { // content-editable div
var range = document.createRange();
range.selectNodeContents(el);
var textNodes = getTextNodesIn(el);
var foundStart = false;
var charCount = 0,
endCharCount;
for (var i = 0, textNode; textNode = textNodes[i++];) {
endCharCount = charCount + textNode.length;
if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length))) {
range.setStart(textNode, start - charCount);
foundStart = true;
}
if (foundStart && end <= endCharCount) {
range.setEnd(textNode, end - charCount);
break;
}
charCount = endCharCount;
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else { // textarea
el.selectionStart = start;
el.selectionEnd = end;
}
}
备注:
<div>
将包含子元素(主要是<br>
s)。我的问题:
我花了几个小时搜索这个并发现没什么用处。有些是关于设置孩子的开始或结束div
,但对我来说,它可以是任何地方,任何地方。
更新:
感谢大家终于finished development!
答案 0 :(得分:11)
这是一种更简单的方法。有几点需要注意:
keypress
是唯一能够可靠地检测键入了哪个字符的关键事件。 keyup
和keydown
不会做。keypress
事件的默认操作来手动处理括号/括号的插入。演示:
代码:
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 selection
var range = sel.getRangeAt(0);
range.deleteContents();
// Insert a text node at the caret containing the braces/parens
var text = (charTyped == "{") ? "{}" : "()";
var textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move the selection to the middle of the inserted text node
range.setStart(textNode, 1);
range.setEnd(textNode, 1);
sel.removeAllRanges();
sel.addRange(range);
}
}
}, false);
答案 1 :(得分:4)
要完成摘要中所述的目标,请尝试更改当前光标位置的节点值。由于您的代码附加到keyup
事件,因此您可以放心,该范围已经折叠并且在文本节点上(所有这些都发生在按键上,这可能已经被触发)。
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;
var char, keyCode;
keyCode = e.keyCode;
char =
(e.shiftKey ? {
"222": '"',
"57": ')',
"219": '}'
} : {
"219": "]"
})[keyCode] || null;
if (char) {
insertChar(char);
}
}
document.getElementById("editable").onkeyup = handleKeyUp;
此外,我发现您使用innerHTML
设置新值(在Element.prototype.setText
中)。这对我来说听起来很震惊! innerHTML
完全核实了以前容器中的任何内容。由于光标绑定到特定元素,并且这些元素刚刚被修改,浏览器应该做什么?如果您完全关心光标最终结束的位置,请尽量避免使用此功能。
关于highlightText
问题,很难说它为什么会被打破。你的小提琴没有显示它被用在任何地方,我需要看到它的用途,以进一步诊断它。但是,我知道可能出现的问题:
我认为你应该仔细看看getCaretPosition
。您将此视为返回光标位置,但这并不是它的作用。请记住,对于浏览器,光标位置始终范围。 总是有一个开头和一个结尾。有时,当范围折叠时,开始和结束是相同的点。但是,您可以获得光标位置并将其视为单点的想法是一种危险的过度简化。
getCaretPosition
还有另一个问题。对于您的可编辑div
,它会执行以下操作:
toString()
并返回结果字符串的长度。如您所知,某些元素(例如<br />
)会影响toString()
的结果。某些元素(例如<span></span>
)没有。为了使计算正确,您需要为某些元素类型而不是其他元素类型微调它。这将是混乱和复杂的。如果您想要一个可以提供给highlightText
的号码并让它按预期工作,那么您当前的getCaretPosition
可能不会有所帮助。
相反,我认为你应该尝试直接使用光标的起点和终点作为两个独立的位置,并相应地更新highlightText
。放弃当前的getCaretPosition
并直接使用浏览器的range.startContainer
,range.startOffset
,range.endContainer
和range.endOffset
原生概念。