解决contenteditable中getSelection获取光标节点及索引不符问题
问题:获取contenteditable元素内光标所在的LI节点及文本索引
我们有一个设置了contenteditable="true"的div,内部包含如下UL结构:
<div contenteditable="true"> <ul> <li>Some text here</li> <li>Some more te|<-- CARET HERExt</li> </ul> </div>
需求是获取光标所在的<li>节点,以及光标在该节点innerText中的索引。当前使用的JavaScript代码如下:
let selection = getSelection(); console.log(selection.focusNode); console.log(selection.focusOffset);
预期输出:
<li> 12
但实际输出却是:
<ul> 3
解决方案
出现这个问题的核心原因是:浏览器在contenteditable环境中,光标可能会被定位到元素节点(比如<ul>)而非内部的文本节点,导致focusNode和focusOffset无法直接对应到目标<li>和文本索引。
可以通过以下代码调整,精准获取目标节点和文本索引:
function getCursorInLi() { const selection = getSelection(); if (selection.rangeCount === 0) return null; let range = selection.getRangeAt(0); let currentNode = range.focusNode; let currentOffset = range.focusOffset; // 递归定位到文本节点,同时调整偏移量 while (currentNode.nodeType !== Node.TEXT_NODE) { currentNode = currentOffset >= currentNode.childNodes.length ? currentNode.lastChild : currentNode.childNodes[currentOffset]; currentOffset = 0; range.setStart(currentNode, currentOffset); } // 找到所在的<li>父节点 const targetLi = currentNode.closest('li'); if (!targetLi) return null; // 计算光标在<li>的innerText中的总索引 let totalOffset = currentOffset; let prevNode = currentNode.previousSibling; // 累加当前文本节点之前所有节点的文本长度 while (prevNode) { if (prevNode.nodeType === Node.TEXT_NODE) { totalOffset += prevNode.textContent.length; } else if (prevNode.nodeType === Node.ELEMENT_NODE) { totalOffset += prevNode.innerText.length; } prevNode = prevNode.previousSibling; } return { liNode: targetLi, textIndex: totalOffset }; } // 调用示例 const result = getCursorInLi(); if (result) { console.log(result.liNode); // 输出目标<li>元素 console.log(result.textIndex); // 输出12 }
代码说明
- 定位文本节点:通过循环判断节点类型,将光标所在的节点逐步定位到最内层的文本节点,确保后续计算基于纯文本内容。
- 找到父级LI:使用
closest('li')快速定位光标所在的<li>元素。 - 计算总索引:遍历当前文本节点之前的所有兄弟节点,累加它们的文本长度,再加上光标在当前文本节点的偏移量,最终得到光标在
<li>的innerText中的总索引。
内容的提问来源于stack exchange,提问作者Existn't
相关产品推荐
相关产品推荐

