Javascript 核算`<;br>`s位于可编辑插入符号位置

Javascript 核算`<;br>`s位于可编辑插入符号位置,javascript,ecmascript-6,contenteditable,caret,Javascript,Ecmascript 6,Contenteditable,Caret,为了在contenteditable元素中获取和设置插入符号位置,我尝试了源代码,但是当您移动到不同的文本节点时,开始和结束位置会重置 <div contenteditable>012345<br><br><br>9012345</div> setCaret功能修改似乎工作正常(在此基本示例中) 函数setCaret(el、开始、结束){ 变量节点,i,nextCharIndex,sel, charIndex=0, nodeStack

为了在contenteditable元素中获取和设置插入符号位置,我尝试了源代码,但是当您移动到不同的文本节点时,开始和结束位置会重置

<div contenteditable>012345<br><br><br>9012345</div>
setCaret
功能修改似乎工作正常(在此基本示例中)

函数setCaret(el、开始、结束){
变量节点,i,nextCharIndex,sel,
charIndex=0,
nodeStack=[el],
foundStart=false,
停止=错误,
range=document.createRange();
范围。设置开始(el,0);
范围。塌陷(真);
而(!stop&&(node=nodeStack.pop()){
//BR不计算在内,因此我们需要增加索引
//遇到
if(node.nodeType===1&&node.nodeName====BR'){
charIndex++;
}else if(node.nodeType==3){
nextCharIndex=charIndex+node.length;

如果(!foundStart&&start>=charIndex&&start=charIndex&&end我修改了您的演示,将位置序列化为一个容器/偏移量对,而不仅仅是一个位置。容器作为一个简单的索引数组序列化到从引用节点开始的每个节点的
childNodes
集合中(在本例中,它当然是
contenteditable
元素)

我还不完全清楚你打算用这个做什么,但是因为它反映了选择模型,所以希望它能给你带来更少的痛苦

const$el=$('ce'),
$startContainer=$('start-container'),
$startOffset=$('start-offset'),
$endContainer=$('end-container'),
$endOffset=$('end-offset');
函数路径FromNode(节点,引用){
功能遍历(节点,acc){
if(节点===引用){
返回acc;
}否则{
const parent=node.parentNode;
const index=[…parent.childNodes].indexOf(node);
返回遍历(父级,[索引,…acc]);
}
}
返回遍历(节点,[]);
}
函数nodeFromPath(路径、引用){
if(path.length==0){
返回参考;
}否则{
常数[index,…rest]=路径;
const next=reference.childNodes[index];
返回nodeFromPath(rest,next);
}
}
函数getCaret(el){
常量范围=document.getSelection().getRangeAt(0);
返回{
开始:{
容器:pathFromNode(range.startContainer,el),
偏移量:range.startOffset
},
完:{
容器:pathFromNode(range.endContainer,el),
偏移量:range.endOffset
}
};
}
函数setCaret(el、开始、结束){
常量范围=document.createRange();
range.setStart(nodeFromPath(start.container,el)、start.offset);
range.setEnd(nodeFromPath(end.container,el),end.offset);
sel=document.getSelection();
选择removeAllRanges();
选择添加范围(范围);
}
函数更新(){
const pos=getCaret($el);
$startContainer.value=JSON.stringify(pos.start.container);
$startOffset.value=pos.start.offset;
$endContainer.value=JSON.stringify(pos.end.container);
$endOffset.value=pos.end.offset;
}
$el.addEventListener('keyup',更新);
$el.addEventListener('单击',更新);
$('set')。addEventListener('click',()=>{
常数开始={
容器:JSON.parse($startContainer.value),
偏移量:$startOffset.value
};
常数结束={
容器:JSON.parse($endContainer.value),
偏移量:$endOffset.value
};
setCaret($el,开始,结束);
});
函数$(sel){
返回文档.getElementById(sel);
}
输入{
宽度:40px;
}
[内容可编辑]{
空白:预处理;
}
(单击和键控更新)
开始:
结束:
设置



12345


9012345
问题的核心似乎是您试图使用一个简单的整数偏移量作为光标位置。HTML是一个复杂的嵌套结构。如果您想要精确的位置,您需要更多的数据。您需要一个特定父元素的偏移量。您是否考虑过更改表示sele的方式Action location是否为每个位置使用父元素+偏移量对?请详细说明。这两个函数都会遍历DOM树以确定偏移量,我不确定您在描述什么…我仍在尝试确定如何确定文本节点中插入符号的位置。@loganfsmyth您介意我使用您的注释和f吗我想它的核心是你所说的:OP是指两个文本节点,它们之间有

元素,作为一个文本节点,我称之为类别错误。@Ed。试试看,没问题。谢谢!当

s包装时,它似乎工作得很好,但内联

s;但我想我可以接受。如果你想知道,我需要这些代码来让我的项目与contenteditable元素正常工作。当从br或blank(无文本)调用setCaret方法时,它不起作用。其他情况就像一个符咒!!你能修复它吗,你能救我一命;)
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};
}
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);
}
<div contenteditable><br>12345<br><br><br>9012345</div>
<div contenteditable><div><br></div>12345<div><br></div><div><br></div><div><br></div>9012345</div>