如何获取textarea、input及contenteditable div中文本位置(类Grammarly)
实现文本元素中特定单词的位置获取(类似Grammarly)
要获取input、textarea或contenteditable="true"的div里目标单词的left/top坐标,核心是先定位文本范围,再将其转化为页面绝对位置。以下分场景给出具体实现:
1. 处理 input/textarea 表单控件
这类控件无法直接用Range API操作文本,我们可以通过克隆元素模拟布局的方式计算位置:
function getWordPositionInInput(element, targetWord) { const fullText = element.value; const wordStartIdx = fullText.indexOf(targetWord); if (wordStartIdx === -1) return null; // 创建和原元素样式完全一致的克隆体,隐藏在页面外 const clone = element.cloneNode(false); Object.assign(clone.style, { position: 'absolute', visibility: 'hidden', top: '0', left: '0', // 确保继承原元素的所有样式,避免布局偏差 font: getComputedStyle(element).font, padding: getComputedStyle(element).padding, border: getComputedStyle(element).border, boxSizing: getComputedStyle(element).boxSizing }); // 填充目标单词之前的文本,用来计算起始位置的宽度 clone.value = fullText.slice(0, wordStartIdx); document.body.appendChild(clone); // 计算最终位置:原元素的页面坐标 + 克隆体的宽度(即单词起始left) const elementRect = element.getBoundingClientRect(); const cloneWidth = clone.getBoundingClientRect().width; const position = { top: elementRect.top + window.scrollY, left: elementRect.left + cloneWidth + window.scrollX, // 可选:获取单词本身的宽度 wordWidth: (clone.value = targetWord).getBoundingClientRect().width }; document.body.removeChild(clone); return position; }
2. 处理 contenteditable="true" 的 div
这类元素支持原生Range API,可以直接定位文本范围并获取坐标:
function getWordPositionInContentEditable(element, targetWord) { // 递归遍历元素,找到包含目标单词的文本节点 function findTextNode(node) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent.includes(targetWord)) return node; } for (const child of node.childNodes) { const result = findTextNode(child); if (result) return result; } return null; } const textNode = findTextNode(element); if (!textNode) return null; const text = textNode.textContent; const wordStartIdx = text.indexOf(targetWord); if (wordStartIdx === -1) return null; // 创建Range并选中目标单词 const range = document.createRange(); range.setStart(textNode, wordStartIdx); range.setEnd(textNode, wordStartIdx + targetWord.length); // 获取单词的页面坐标(加上滚动偏移量得到绝对位置) const rect = range.getBoundingClientRect(); return { top: rect.top + window.scrollY, left: rect.left + window.scrollX, width: rect.width, height: rect.height }; }
3. 统一调用入口
封装一个函数,自动识别元素类型并调用对应逻辑:
function getWordPosition(element, targetWord) { if (!element || typeof targetWord !== 'string' || targetWord.length === 0) { return null; } const tagName = element.tagName.toLowerCase(); if (tagName === 'input' || tagName === 'textarea') { return getWordPositionInInput(element, targetWord); } else if (element.isContentEditable) { return getWordPositionInContentEditable(element, targetWord); } return null; }
关键注意事项
- 克隆input/textarea时,必须完全同步原元素的样式(字体、行高、内边距等),否则位置计算会出现偏差
- 对于嵌套复杂的contenteditable div,递归遍历文本节点的逻辑可以确保找到目标单词
- 页面滚动时,必须加上
window.scrollX和window.scrollY,将相对视口的坐标转为页面绝对坐标 - 上述代码默认返回目标单词第一次出现的位置,若需要获取所有出现位置,只需遍历文本中所有匹配的索引即可
内容的提问来源于stack exchange,提问作者JokerLiang
相关产品推荐
相关产品推荐

