【发布时间】:2017-09-26 19:56:15
【问题描述】:
要获取和设置 contenteditable 元素中的插入符号位置,我尝试了 this answer 中的代码,但是当您移动到不同的文本节点时,开始和结束位置会重置。
<div contenteditable>012345<br><br><br>9012345</div>
所以,我从this answer(@TimDown)修改了代码,但它仍然不完全正确计算换行符...在this demo 中,当我点击@987654329 之后@ 并按三次右箭头,我将看到开始/结束报告为5、6,然后是8。或者,使用鼠标从第一行的4中选择并继续向右选择(见gif)
这是代码(demo;尽管看起来像,但 没有在使用 jQuery)
function getCaret(el) {
let start, end;
const range = document.getSelection().getRangeAt(0),
preSelectionRange = range.cloneRange(),
postSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(el);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
postSelectionRange.selectNodeContents(el);
postSelectionRange.setEnd(range.endContainer, range.endOffset);
start = preSelectionRange.toString().length;
end = start + range.toString().length;
// count <br>'s and adjust start & end
if (start > 0) {
var node,
i = el.children.length;
while (i--) {
node = el.children[i];
if (node.nodeType === 1 && node.nodeName === 'BR') {
start += preSelectionRange.intersectsNode(el.children[i]) ? 1 : 0;
end += postSelectionRange.intersectsNode(el.children[i]) ? 1 : 0;
}
}
}
return {start, end};
}
setCaret 函数修改似乎工作正常(在这个基本的 contenteditable 示例中)。
function setCaret(el, start, end) {
var node, i, nextCharIndex, sel,
charIndex = 0,
nodeStack = [el],
foundStart = false,
stop = false,
range = document.createRange();
range.setStart(el, 0);
range.collapse(true);
while (!stop && (node = nodeStack.pop())) {
// BR's aren't counted, so we need to increase the index when one
// is encountered
if (node.nodeType === 1 && node.nodeName === 'BR') {
charIndex++;
} else if (node.nodeType === 3) {
nextCharIndex = charIndex + node.length;
if (!foundStart && start >= charIndex && start <= nextCharIndex) {
range.setStart(node, start - charIndex);
foundStart = true;
}
if (foundStart && end >= charIndex && end <= nextCharIndex) {
range.setEnd(node, end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
我可以针对以下问题使用一些建议/帮助:
- 如何正确计算
<br>s? -
如何计算开头的
<br>(在此 HTML 示例中)?<div contenteditable><br>12345<br><br><br>9012345</div> -
将
<br>包含在<div>中(在此HTML 示例中)——我最终会谈到这一点,但我不想继续沿着这条路走下去,发现有更简单的方法。<div contenteditable><div><br></div>12345<div><br></div><div><br></div><div><br></div>9012345</div> 我尝试将上面的代码替换为
rangy,但它似乎没有获取或设置范围的内置方法。
【问题讨论】:
-
问题的核心似乎是您尝试使用简单的整数偏移量作为光标位置。 HTML 是一个复杂的嵌套结构。如果你想要一个准确的位置,你需要更多的数据。您需要与特定父元素的偏移量。您是否考虑过更改表示选择位置的方式,以便为每个位置使用父元素+偏移对?
-
请您详细说明一下。这两个函数都遍历 DOM 树来确定偏移量,我不确定你在描述什么......我仍在试图弄清楚如何确定文本节点中的插入符号位置。
-
@loganfsmyth 你介意我带着你的评论跑并填写答案吗?我认为这就是您所说的核心:OP 指的是两个文本节点之间有
<br>元素作为单个文本节点,我称之为类别错误。 -
@Ed。去吧,没问题。
标签: javascript ecmascript-6 contenteditable caret