如何获取 <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, '&nbsp;')
        .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 协同处理,以便正确模拟文本中的换行和空格等格式效果。

comments powered by Disqus

© 2024 - 2025 GuanCun 的博客

🌱 Powered by Hugo with theme Dream.

关于我

当前博客网站还在测试阶段

社交链接