如何获取 <textarea> 内部光标的页面坐标
2024-11-10T23:57:50+08:00 | 2分钟阅读 | 更新于 2024-11-10T23:57:50+08:00
通常,我们能直接获取元素在页面上的精确坐标,然而当面对需要获取 <textarea> 内部光标的页面坐标时,我们遇到了麻烦。
涉及知识
背景
在一个项目中,我需要实现获取 <textarea> 中光标的具体页面坐标。通过 HTMLInputElement 对象,只能获取到光标位置相关的属性,无法直接得到光标在页面上的坐标。
通常,我们可以通过 getBoundingClientRect 可以获取元素相对于视口的位置,它能返回一个 DOMRect 对象,包含元素在页面上的精确坐标。
示例代码:
// 获取元素
const element = document.getElementById("yourElementId");
// 获取元素相对于视口的位置
const rect = element.getBoundingClientRect();
// 输出位置
console.log("Element Position:");
console.log("Top: " + rect.top);
console.log("Left: " + rect.left);
console.log("Width: " + rect.width);
console.log("Height: " + rect.height);
既然 <textarea> 内部光标位置无法直接获取页面坐标,那我们是不是可以通过实时模仿 <textarea> 在页面输入的内容,创造两个行内元素,一个模拟 <textarea> 文本内容,另一个模拟光标的位置。
因为模拟光标是标准 DOM 元素,我们可以直接拿到它的真实坐标。
关键步骤
1. 模拟 <textarea> 样式和内容:
- 创建影子元素,将 <textarea> 样式和内容同步到这个影子元素中。
2. 动态更新虚拟光标位置:
- 在每次 <textarea> 事件(输入、键盘、点击)时,更新影子元素中的文本内容,填充到虚拟光标前的位置。
3. 实时获取光标的真实坐标:
- 通过
getBoundingClientRect方法获取虚拟光标的坐标。
代码示例
线上 CodePen: 实时获取 <textarea> 光标位置的代码示例
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>实时获取 textarea 光标位置</title>
<style>
#textarea {
width: 300px;
height: 100px;
font-size: 14px;
}
#textareaShadow {
width: 300px;
height: 100px;
border: 1px solid #ccc;
padding: 2px;
font-size: 14px;
/* 确保文本在达到边界时能够换行 */
overflow-wrap: break-word;
}
#inputAnalog {
background-color: #ccc;
}
</style>
</head>
<body>
<h2 id="cursorPosition">光标位置: (0, 0)</h2>
<textarea id="textarea"></textarea>
<div id="textareaShadow">
<!-- 模拟输入框 -->
<span id="inputAnalog"></span>
<!-- 模拟光标 -->
<span id="cursorAnalog" style="font-weight: bold;">*</span>
</div>
<script>
const textarea = document.getElementById('textarea');
const cursorPositionDiv = document.getElementById('cursorPosition');
const inputAnalog = document.getElementById('inputAnalog');
const cursorAnalog = document.getElementById('cursorAnalog');
const textareaShadow = document.getElementById('textareaShadow');
textarea.addEventListener('input', updateCursorPosition);
textarea.addEventListener('keyup', updateCursorPosition);
textarea.addEventListener('click', updateCursorPosition);
function updateCursorPosition(e) {
const selectionStart = e.target.selectionStart;
// 更新 shadow 中的内容以匹配 textarea 的内容
inputAnalog.innerHTML = e.target.value.substring(0, selectionStart)
.replace(/ /g, ' ')
.replace(/\n/g, '<br>');;
const rect = cursorAnalog.getBoundingClientRect()
cursorPositionDiv.innerHTML = `光标位置: (${rect.x}, ${rect.y})`;
}
// 使 shadow 元素的样式与 textarea 一致
const textareaStyle = window.getComputedStyle(textarea);
textareaShadow.style.font = textareaStyle.font;
textareaShadow.style.lineHeight = textareaStyle.lineHeight;
textareaShadow.style.padding = textareaStyle.padding;
</script>
</body>
</html>
局限
这个效果会存在一个局限,当 <textarea> 中连续输入空格直到当前行满时,<textarea> 不会触发换行。而我们的 <textarea> 影子则会因为叠加真实空格而换行,导致显示效果不一致。
总结
在设计虚拟光标时,主要的挑战在于精确模仿 <textarea> 的文本效果,它需要 CSS 和 JS 协同处理,以便正确模拟文本中的换行和空格等格式效果。