繁体   English   中英

获取内容Editable caret position

[英]Get contentEditable caret position

我找到了大量关于如何在contentEditable元素中设置插入符号 position 的跨浏览器的好答案,但没有关于如何首先获取插入符号 position 的答案。

我想要做的是知道keyup上的 div 中的插入符 position。 因此,当用户键入文本时,我可以随时知道contentEditable元素中的插入符号 position。

<div id="contentBox" contentEditable="true"></div>

$('#contentbox').keyup(function() { 
    // ... ? 
});

以下代码假设:

  • 可编辑<div>始终只有一个文本节点,没有其他节点
  • 可编辑的 div 没有将 CSS white-space属性设置为pre

如果您需要更通用的方法来处理嵌套元素的内容,请尝试以下答案:

https://stackoverflow.com/a/4812022/96100

代码:

 function getCaretPosition(editableDiv) { var caretPos = 0, sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel.rangeCount) { range = sel.getRangeAt(0); if (range.commonAncestorContainer.parentNode == editableDiv) { caretPos = range.endOffset; } } } else if (document.selection && document.selection.createRange) { range = document.selection.createRange(); if (range.parentElement() == editableDiv) { var tempEl = document.createElement("span"); editableDiv.insertBefore(tempEl, editableDiv.firstChild); var tempRange = range.duplicate(); tempRange.moveToElementText(tempEl); tempRange.setEndPoint("EndToEnd", range); caretPos = tempRange.text.length; } } return caretPos; }
 #caretposition { font-weight: bold; }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div> <div id="caretposition">0</div> <script> var update = function() { $('#caretposition').html(getCaretPosition(this)); }; $('#contentbox').on("mousedown mouseup keydown keyup", update); </script>

我在其他答案中没有看到的一些皱纹:

  1. 该元素可以包含多个级别的子节点(例如,具有子节点的子节点具有子节点......)
  2. 选择可以由不同的开始和结束位置组成(例如选择多个字符)
  3. 包含插入符号开始/结束的节点可能不是元素或其直接子元素

这是一种获取开始和结束位置作为元素 textContent 值的偏移量的方法:

// node_walk: walk the element tree, stop when func(node) returns false
function node_walk(node, func) {
  var result = func(node);
  for(node = node.firstChild; result !== false && node; node = node.nextSibling)
    result = node_walk(node, func);
  return result;
};

// getCaretPosition: return [start, end] as offsets to elem.textContent that
//   correspond to the selected portion of text
//   (if start == end, caret is at given position and no text is selected)
function getCaretPosition(elem) {
  var sel = window.getSelection();
  var cum_length = [0, 0];

  if(sel.anchorNode == elem)
    cum_length = [sel.anchorOffset, sel.extentOffset];
  else {
    var nodes_to_find = [sel.anchorNode, sel.extentNode];
    if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
      return undefined;
    else {
      var found = [0,0];
      var i;
      node_walk(elem, function(node) {
        for(i = 0; i < 2; i++) {
          if(node == nodes_to_find[i]) {
            found[i] = true;
            if(found[i == 0 ? 1 : 0])
              return false; // all done
          }
        }

        if(node.textContent && !node.firstChild) {
          for(i = 0; i < 2; i++) {
            if(!found[i])
              cum_length[i] += node.textContent.length;
          }
        }
      });
      cum_length[0] += sel.anchorOffset;
      cum_length[1] += sel.extentOffset;
    }
  }
  if(cum_length[0] <= cum_length[1])
    return cum_length;
  return [cum_length[1], cum_length[0]];
}

 $("#editable").on('keydown keyup mousedown mouseup',function(e){ if($(window.getSelection().anchorNode).is($(this))){ $('#position').html('0') }else{ $('#position').html(window.getSelection().anchorOffset); } });
 body{ padding:40px; } #editable{ height:50px; width:400px; border:1px solid #000; } #editable p{ margin:0; padding:0; }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script> <div contenteditable="true" id="editable">move the cursor to see position</div> <div> position : <span id="position"></span> </div>

参加聚会有点晚,但以防万一其他人正在挣扎。 我在过去两天找到的所有 Google 搜索都没有提出任何有效的方法,但我提出了一个简洁而优雅的解决方案,无论您有多少嵌套标签,该解决方案始终有效:

 function cursor_position() { var sel = document.getSelection(); sel.modify("extend", "backward", "paragraphboundary"); var pos = sel.toString().length; if(sel.anchorNode != undefined) sel.collapseToEnd(); return pos; } // Demo: var elm = document.querySelector('[contenteditable]'); elm.addEventListener('click', printCaretPosition) elm.addEventListener('keydown', printCaretPosition) function printCaretPosition(){ console.log( cursor_position(), 'length:', this.textContent.trim().length ) }
 <div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

它一直选择回到段落的开头,然后计算字符串的长度以获取当前位置,然后撤消选择以将光标返回到当前位置。 如果您想为整个文档(多个段落)做到这一点,那么改变paragraphboundarydocumentboundary或您的案件无论粒度。 查看 API 了解更多详情 干杯! :)

尝试这个:

Caret.js 从文本字段获取插入符号位置和偏移量

https://github.com/ichord/Caret.js

演示: http : //ichord.github.com/Caret.js

window.getSelection - vs - document.selection

这个对我有用:

 function getCaretCharOffset(element) { var caretOffset = 0; if (window.getSelection) { var range = window.getSelection().getRangeAt(0); var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); caretOffset = preCaretRange.toString().length; } else if (document.selection && document.selection.type != "Control") { var textRange = document.selection.createRange(); var preCaretTextRange = document.body.createTextRange(); preCaretTextRange.moveToElementText(element); preCaretTextRange.setEndPoint("EndToEnd", textRange); caretOffset = preCaretTextRange.text.length; } return caretOffset; } // Demo: var elm = document.querySelector('[contenteditable]'); elm.addEventListener('click', printCaretPosition) elm.addEventListener('keydown', printCaretPosition) function printCaretPosition(){ console.log( getCaretCharOffset(elm), 'length:', this.textContent.trim().length ) }
 <div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

调用行取决于事件类型,对于关键事件使用此:

getCaretCharOffsetInDiv(e.target) + ($(window.getSelection().getRangeAt(0).startContainer.parentNode).index());

对于鼠标事件使用这个:

getCaretCharOffsetInDiv(e.target.parentElement) + ($(e.target).index())

在这两种情况下,我通过添加目标索引来处理中断线

function getCaretPosition() {
    var x = 0;
    var y = 0;
    var sel = window.getSelection();
    if(sel.rangeCount) {
        var range = sel.getRangeAt(0).cloneRange();
        if(range.getClientRects()) {
        range.collapse(true);
        var rect = range.getClientRects()[0];
        if(rect) {
            y = rect.top;
            x = rect.left;
        }
        }
    }
    return {
        x: x,
        y: y
    };
}
//global savedrange variable to store text range in
var savedrange = null;

function getSelection()
{
    var savedRange;
    if(window.getSelection && window.getSelection().rangeCount > 0) //FF,Chrome,Opera,Safari,IE9+
    {
        savedRange = window.getSelection().getRangeAt(0).cloneRange();
    }
    else if(document.selection)//IE 8 and lower
    { 
        savedRange = document.selection.createRange();
    }
    return savedRange;
}

$('#contentbox').keyup(function() { 
    var currentRange = getSelection();
    if(window.getSelection)
    {
        //do stuff with standards based object
    }
    else if(document.selection)
    { 
        //do stuff with microsoft object (ie8 and lower)
    }
});

注意:range 对象本身可以存储在一个变量中,并且可以随时重新选择,除非 contenteditable div 的内容发生变化。

IE 8 及更低版本的参考: http : //msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx

标准(所有其他)浏览器的参考: https : //developer.mozilla.org/en/DOM/range (它是 mozilla 文档,但代码也适用于 chrome、safari、opera 和 ie9)

由于这让我花了很长时间才弄清楚使用新的window.getSelection API,我将分享给后代。 请注意,MDN 建议对 window.getSelection 有更广泛的支持,但是,您的里程可能会有所不同。

const getSelectionCaretAndLine = () => {
    // our editable div
    const editable = document.getElementById('editable');

    // collapse selection to end
    window.getSelection().collapseToEnd();

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

    // get anchor node if startContainer parent is editable
    let selectedNode = editable === range.startContainer.parentNode
      ? sel.anchorNode 
      : range.startContainer.parentNode;

    if (!selectedNode) {
        return {
            caret: -1,
            line: -1,
        };
    }

    // select to top of editable
    range.setStart(editable.firstChild, 0);

    // do not use 'this' sel anymore since the selection has changed
    const content = window.getSelection().toString();
    const text = JSON.stringify(content);
    const lines = (text.match(/\\n/g) || []).length + 1;

    // clear selection
    window.getSelection().collapseToEnd();

    // minus 2 because of strange text formatting
    return {
        caret: text.length - 2, 
        line: lines,
    }
} 

这是一个在 keyup 上触发的jsfiddle 但是请注意,快速方向键按下以及快速删除似乎是跳过事件。

一种直接的方式,它遍历 contenteditable div 的所有孩子,直到它到达 endContainer。 然后我添加结束容器偏移量,我们就有了字符索引。 应该使用任意数量的嵌套。 使用递归。

注意:需要多边形填充以支持Element.closest('div[contenteditable]')

https://codepen.io/alockwood05/pen/vMpdmZ

function caretPositionIndex() {
    const range = window.getSelection().getRangeAt(0);
    const { endContainer, endOffset } = range;

    // get contenteditableDiv from our endContainer node
    let contenteditableDiv;
    const contenteditableSelector = "div[contenteditable]";
    switch (endContainer.nodeType) {
      case Node.TEXT_NODE:
        contenteditableDiv = endContainer.parentElement.closest(contenteditableSelector);
        break;
      case Node.ELEMENT_NODE:
        contenteditableDiv = endContainer.closest(contenteditableSelector);
        break;
    }
    if (!contenteditableDiv) return '';


    const countBeforeEnd = countUntilEndContainer(contenteditableDiv, endContainer);
    if (countBeforeEnd.error ) return null;
    return countBeforeEnd.count + endOffset;

    function countUntilEndContainer(parent, endNode, countingState = {count: 0}) {
      for (let node of parent.childNodes) {
        if (countingState.done) break;
        if (node === endNode) {
          countingState.done = true;
          return countingState;
        }
        if (node.nodeType === Node.TEXT_NODE) {
          countingState.count += node.length;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
          countUntilEndContainer(node, endNode, countingState);
        } else {
          countingState.error = true;
        }
      }
      return countingState;
    }
  }

如果将可编辑的 div 样式设置为“display:inline-block; white-space: pre-wrap”,则在输入新行时不会获得新的子 div,只会获得 LF 字符(即 &#10); .

 function showCursPos(){ selection = document.getSelection(); childOffset = selection.focusOffset; const range = document.createRange(); eDiv = document.getElementById("eDiv"); range.setStart(eDiv, 0); range.setEnd(selection.focusNode, childOffset); var sHtml = range.toString(); p = sHtml.length; sHtml=sHtml.replace(/(\\r)/gm, "\\\\r"); sHtml=sHtml.replace(/(\\n)/gm, "\\\\n"); document.getElementById("caretPosHtml").value=p; document.getElementById("exHtml").value=sHtml; }
 click/type in div below: <br> <div contenteditable name="eDiv" id="eDiv" onkeyup="showCursPos()" onclick="showCursPos()" style="width: 10em; border: 1px solid; display:inline-block; white-space: pre-wrap; " >123&#13;&#10;456&#10;789</div> <p> html caret position:<br> <input type="text" id="caretPosHtml"> <p> html from start of div:<br> <input type="text" id="exHtml">

我注意到当您在可编辑的 div 中按“enter”键时,它会创建一个新节点,因此 focusOffset 重置为零。 这就是为什么我必须添加一个范围变量,并将其从子节点的 focusOffset 扩展回 eDiv 的开头(从而捕获其间的所有文本)。

这个建立在@alockwood05 的答案之上,并为在 contenteditable div 内带有嵌套标签的插入符号以及节点内的偏移量提供 get 和 set 功能,以便您拥有一个既可通过偏移量序列化又可反序列化的解决方案。

我在跨平台代码编辑器中使用此解决方案,该编辑器需要在通过词法分析器/解析器突出显示语法之前获取插入符号开始/结束位置,然后立即将其设置回来。

function countUntilEndContainer(parent, endNode, offset, countingState = {count: 0}) {
    for (let node of parent.childNodes) {
        if (countingState.done) break;
        if (node === endNode) {
            countingState.done = true;
            countingState.offsetInNode = offset;
            return countingState;
        }
        if (node.nodeType === Node.TEXT_NODE) {
            countingState.offsetInNode = offset;
            countingState.count += node.length;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            countUntilEndContainer(node, endNode, offset, countingState);
        } else {
            countingState.error = true;
        }
    }
    return countingState;
}

function countUntilOffset(parent, offset, countingState = {count: 0}) {
    for (let node of parent.childNodes) {
        if (countingState.done) break;
        if (node.nodeType === Node.TEXT_NODE) {
            if (countingState.count <= offset && offset < countingState.count + node.length)
            {
                countingState.offsetInNode = offset - countingState.count;
                countingState.node = node; 
                countingState.done = true; 
                return countingState; 
            }
            else { 
                countingState.count += node.length; 
            }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            countUntilOffset(node, offset, countingState);
        } else {
            countingState.error = true;
        }
    }
    return countingState;
}

function getCaretPosition()
{
    let editor = document.getElementById('editor');
    let sel = window.getSelection();
    if (sel.rangeCount === 0) { return null; }
    let range = sel.getRangeAt(0);    
    let start = countUntilEndContainer(editor, range.startContainer, range.startOffset);
    let end = countUntilEndContainer(editor, range.endContainer, range.endOffset);
    let offsetsCounts = { start: start.count + start.offsetInNode, end: end.count + end.offsetInNode };
    let offsets = { start: start, end: end, offsets: offsetsCounts };
    return offsets;
}

function setCaretPosition(start, end)
{
    let editor = document.getElementById('editor');
    let sel = window.getSelection();
    if (sel.rangeCount === 0) { return null; }
    let range = sel.getRangeAt(0);
    let startNode = countUntilOffset(editor, start);
    let endNode = countUntilOffset(editor, end);
    let newRange = new Range();
    newRange.setStart(startNode.node, startNode.offsetInNode);
    newRange.setEnd(endNode.node, endNode.offsetInNode);
    sel.removeAllRanges();
    sel.addRange(newRange);
    return true;
}

这个适用于角度

private getCaretPosition() {
   let caretRevCount = 0;
   if (window.getSelection) {
      const selection = window.getSelection();
      const currentNode = selection.focusNode.parentNode;
      caretRevCount = selection.focusOffset;
      let previousNode = currentNode.previousSibling;
      while(previousNode && previousNode.nodeName === 'SPAN') { 
      // you can check specific element
      caretRevCount += previousNode.textContent.length;
      previousNode = previousNode.previousSibling;
      }
    }
    return caretRevCount;
}

此答案使用递归函数处理嵌套文本元素。 🪄

奖励:将插入符号位置设置为保存位置。

 function getCaretData(elem) { var sel = window.getSelection(); return [sel.anchorNode, sel.anchorOffset]; } function setCaret(el, pos) { var range = document.createRange(); var sel = window.getSelection(); range.setStart(el,pos); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } let indexStack = []; function checkParent(elem) { let parent = elem.parentNode; let parentChildren = Array.from(parent.childNodes); let elemIndex = parentChildren.indexOf(elem); indexStack.unshift(elemIndex); if (parent !== cd) { checkParent(parent); } else { return; } } let stackPos = 0; let elemToSelect; function getChild(parent, index) { let child = parent.childNodes[index]; if (stackPos < indexStack.length-1) { stackPos++; getChild(child, indexStack[stackPos]); } else { elemToSelect = child; return; } } let cd = document.querySelector('.cd'), caretpos = document.querySelector('.caretpos'); cd.addEventListener('keyup', () => { let caretData = getCaretData(cd); let selectedElem = caretData[0]; let caretPos = caretData[1]; indexStack = []; checkParent(selectedElem); cd.innerHTML = 'Hello world! <span>Inline! <span>In inline!</span></span>'; stackPos = 0; getChild(cd, indexStack[stackPos]); setCaret(elemToSelect, caretPos); caretpos.innerText = 'indexStack: ' + indexStack + '. Got child: ' + elemToSelect.data + '. Moved caret to child at pos: ' + caretPos; })
 .cd, .caretpos { font-family: system-ui, Segoe UI, sans-serif; padding: 10px; } .cd span { display: inline-block; color: purple; padding: 5px; } .cd span span { color: chocolate; padding: 3px; } :is(.cd, .cd span):hover { border-radius: 3px; box-shadow: inset 0 0 0 2px #005ecc; }
 <div class="cd" contenteditable="true">Hello world! <span>Inline! <span>In inline!</span></span></div> <div class="caretpos">Move your caret inside the elements above ⤴</div>

代码笔

我使用了John Ernest的优秀代码,并根据我的需要对其进行了一些修改:

  • 使用 TypeScript(在 Angular 应用程序中);
  • 使用稍微不同的数据结构。

在研究它时,我偶然发现了鲜为人知(或很少使用)的 TreeWalker,并进一步简化了它,因为它允许摆脱递归。

一种可能的优化可能是遍历树一次以找到开始节点和结束节点,但是:

  • 我怀疑用户是否能感觉到速度的提升,即使是在一个巨大而复杂的页面的末尾;
  • 它会使算法更复杂,可读性更差。

相反,我处理了开始与结束相同的情况(只是一个插入符号,没有真正的选择)。

这是代码:

export type CountingState = {
    countBeforeNode: number;
    offsetInNode: number;
    node?: Node;
};

export type RangeOffsets = {
    start: CountingState;
    end: CountingState;
    offsets: { start: number; end: number; }
};

export function getCaretPosition(container: Node): RangeOffsets | undefined {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) { return undefined; }
    const range = selection.getRangeAt(0);
    const start = countUntilEndNode(container, range.startContainer, range.startOffset);
    const end = range.collapsed ? start : countUntilEndNode(container, range.endContainer, range.endOffset);
    const offsets = { start: start.countBeforeNode + start.offsetInNode, end: end.countBeforeNode + end.offsetInNode };
    const rangeOffsets: RangeOffsets = { start, end, offsets };
    return rangeOffsets;
}

export function setCaretPosition(container: Node, start: number, end: number): boolean {
    const selection = window.getSelection();
    if (!selection) { return false; }
    const startState = countUntilOffset(container, start);
    const endState = start === end ? startState : countUntilOffset(container, end);
    const range = document.createRange(); // new Range() doesn't work for me!
    range.setStart(startState.node!, startState.offsetInNode);
    range.setEnd(endState.node!, endState.offsetInNode);
    selection.removeAllRanges();
    selection.addRange(range);
    return true;
}

function countUntilEndNode(
    parent: Node,
    endNode: Node,
    offset: number,
    countingState: CountingState = { countBeforeNode: 0, offsetInNode: 0 },
): CountingState {
    const treeWalker = document.createTreeWalker(parent, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT);
    while (treeWalker.nextNode()) {
        const node = treeWalker.currentNode;
        if (node === endNode) {
            // We found the target node, memorize it.
            countingState.node = node;
            countingState.offsetInNode = offset;
            break;
        }
        if (isTextNode(node)) {
            // A simple text node in the way, we add its length to the total until the target node.
            countingState.countBeforeNode += node.length;
        }
    }
    return countingState;
}

function countUntilOffset(
    parent: Node,
    offset: number,
    countingState: CountingState = { countBeforeNode: 0, offsetInNode: 0 },
): CountingState {
    const treeWalker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
    while (treeWalker.nextNode()) {
        const node = treeWalker.currentNode;
        if (isTextNode(node)) {
            if (countingState.countBeforeNode <= offset && offset < countingState.countBeforeNode + node.length) {
                countingState.offsetInNode = offset - countingState.countBeforeNode;
                countingState.node = node;
                break;
            }
            countingState.countBeforeNode += node.length;
        }
    }
    return countingState;
}

function isTextNode(node: Node): node is Text {
    return node.nodeType === Node.TEXT_NODE;
}

因此,根据 Chris Sullivan 提供的答案,我设法创建了一个在通过键盘进行选择时不会重置的版本,并且能够检测到列号和行号。

在这种方法中,您首先必须找出一个解决方案来获取所有文本,直到克拉。 您可以通过获取当前选择(即插入符号)、克隆它的第一个范围、折叠它,然后将范围的起始节点更改为元素的开头来做到这一点。 从那里,您可以通过简单地在范围上运行 toString 来提取直到克拉的所有文本。 现在您有了文本,我们可以对其进行一些简单的计算以确定行号和列。

对于行号,您只需计算文本字符串中的换行符数。 这可以使用一些简单的正则表达式来完成,可以在下面的代码中看到。

对于列号,可以通过三种方式获取“列号”。

  1. 行号的“相对列”,类似于 Windows 记事本的计算方式,是最容易计算的。 这只是范围的结束偏移量( range.endOffset )。
  2. 插入符号的实际 position 相对于您需要按下的箭头键次数才能到达 position。 这可以通过替换文本中的所有换行符,然后获取它的长度来计算。
  3. 插入符号相对于实际文本的实际 position; 你可以通过获取文本的长度来获取它。

废话不多说,现在是表演时间:

// Caret
function getCaretPosition(element) {
    // Check for selection
    if (window.getSelection().type == "None") {
        return {
            "ln": -1,
            "col": -1
        }
    }

    // Copy range
    var selection = window.getSelection();
    var range = selection.getRangeAt(0).cloneRange();

    // Collapse range
    range.collapse();

    // Move range to encompass everything
    range.setStart(element.firstChild, 0);

    // Calculate position
    var content = range.toString();
    var text = JSON.stringify(content);
    var lines = (text.match(/\\n/g) || []).length + 1;

    // Return caret position (col - 2 due to some weird calculation with regex)
    return {
        "ln": lines,
        // "col": range.endOffset + 1 // Method 1
        "col": text.replace(/\\n/g, " ").length - 2 // Method 2
        // "col": text.length -2 // Method 3
    }
}

现在通过这种方法,如果您愿意,您可以在每次更新选择时获取插入符号 position:

document.addEventListener("selectionchange", function(e) {
    console.log(getCaretPosition(document.getElementById("text-area")));
});

我希望这对某人有所帮助,我花了几个小时试图弄清楚如何做到这一点

下面的代码通过获取当前元素的偏移量然后导航回contenteditable内的所有元素并计算字符总数来计算插入符号的位置。

这会:

  • 不破坏格式化功能
  • 处理多行。

如果您遇到问题,请告诉我,以便我更新代码。

function getRowTextLength(currentNode) {
    let previousSibling;
    let textLength = 0;
    //this means we are outside our desired scope
    if (currentNode?.contentEditable == "true") {
        return textLength;
    }
    while (currentNode) {
        //get the previous element of the currentNode
        previousSibling =
            currentNode.previousSibling || //UNFORMATTED text case
            //avoid targetting the contenteditable div itself
            (currentNode.parentNode.nodeName != "DIV"
                ? currentNode.parentNode.previousSibling //FORMATTED text case
                : null);

        //count the number of characters in the previous element, if exists
        textLength = previousSibling
            ? textLength + previousSibling.textContent.length
            : textLength;
        //set current element as previous element
        currentNode = previousSibling;
        //continue looping as long as we have a previous element
    }
    return textLength;
}

//pass e.target from an eventListener as argument
function getCaretPosition(element) {
    let selection = getSelection(element);
    //caret position at current row
    let caretPosition = selection.anchorOffset;
    let currentNode = selection.baseNode;

    caretPosition += getRowTextLength(currentNode);

    //get closest div parent node
    if (caretPosition != 0) {
        do {
            currentNode = currentNode.parentNode;
        } while (currentNode.nodeName != "DIV");
    }

    caretPosition += getRowTextLength(currentNode);

    //console.log("CARET POSITION ", caretPosition);
    return caretPosition;
}

获取相对于可编辑内容的插入符号索引 position:

const getCaretPosition = () => {
  var selection = document.getSelection();
  if (!selection || !divRef) return 0;
  selection.collapseToEnd();
  const range = selection.getRangeAt(0);
  const clone = range.cloneRange();
  clone.selectNodeContents(divRef);
  clone.setEnd(range.startContainer, range.startOffset);
  return clone.toString().length;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM