如何在contenteditable=“true”的元素中改变若干子元素时仍旧保持聚焦状态

50 阅读1分钟
<!DOCTYPE html>
<html>
<head>
  <title>ContentEditable Cursor Demo</title>
  <style>
    #editor {
      border: 1px solid #ccc;
      padding: 10px;
      margin: 10px;
      min-height: 100px;
    }
  </style>
</head>
<body>
  <div id="editor" contenteditable="true"><span>poipiopiop</span><span>ljkljkljkljkl</span></div>
  <button onclick="replaceContent()">替换内容并保持光标</button>

  <script>
    const editor = document.getElementById('editor');

    // 获取光标全局字符偏移量
    function getGlobalOffset() {
      const selection = window.getSelection();
      if (!selection.rangeCount) return 0;
      
      const range = selection.getRangeAt(0);
      let offset = 0;
      const nodeStack = [editor];
      let found = false;

      // 前序遍历所有文本节点
      while (nodeStack.length > 0 && !found) {
        const node = nodeStack.pop();
        
        if (node.nodeType === Node.TEXT_NODE) {
          if (node === range.startContainer) {
            offset += range.startOffset;
            found = true;
          } else {
            offset += node.textContent.length;
          }
        } else {
          // 逆序压栈保证遍历顺序
          for (let i = node.childNodes.length - 1; i >= 0; i--) {
            nodeStack.push(node.childNodes[i]);
          }
        }
      }
      
      return offset;
    }

    // 设置光标到指定偏移量
    function setGlobalOffset(targetOffset) {
      let currentOffset = 0;
      let targetNode = editor;
      let targetNodeOffset = 0;

      // 遍历所有文本节点
      const walker = document.createTreeWalker(
        editor,
        NodeFilter.SHOW_TEXT,
        null,
        false
      );

      let node;
      while ((node = walker.nextNode())) {
        const nodeLength = node.textContent.length;
        if (currentOffset + nodeLength > targetOffset) {
          targetNode = node;
          targetNodeOffset = targetOffset - currentOffset;
          break;
        }
        currentOffset += nodeLength;
      }

      // 处理越界情况(放置到最后一个位置)
      if (!targetNode || targetNode.nodeType !== Node.TEXT_NODE) {
        const allChildren = editor.childNodes;
        targetNode = editor;
        targetNodeOffset = allChildren.length;
      }

      // 设置光标位置
      const range = document.createRange();
      range.setStart(targetNode, targetNodeOffset);
      range.collapse(true);
      
      const selection = window.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);
    }

    // 替换内容的主函数
    function replaceContent() {
      // 保存原始偏移量
      const originalOffset = getGlobalOffset();
      
      // 生成新内容(示例:随机长度的新文本)
      const newText = `<span>asdfasdf</span><span>zxcvzxcvzxcvsadf</span>`;
      editor.innerHTML = newText;

      // 计算新内容总长度
      let newContentLength = 0;
      const walker = document.createTreeWalker(
        editor,
        NodeFilter.SHOW_TEXT,
        null,
        false
      );
      let node;
      while ((node = walker.nextNode())) {
        newContentLength += node.textContent.length;
      }

      // 调整偏移量(如果越界则放到末尾)
      const adjustedOffset = Math.min(originalOffset, newContentLength);
      
      // 恢复光标
      setGlobalOffset(adjustedOffset);
      
      // 保持聚焦
      editor.focus();
    }

  </script>
</body>
</html>