You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Google Docs中显示用户鼠标指针位置?基于Google Apps Script方案问询

实现Google Docs协作者实时指针/位置标识(替代Slides原生功能)

Google Slides原生支持协作者实时鼠标指针显示,但Google Docs无此功能。以下是基于Google Apps Script的两种实现方案,从轻量简化到接近Slides效果,适配不同协作场景:


简化实现方案(轻量无干扰)

无需追踪精确鼠标坐标,基于用户文本光标位置插入临时标识,完全不干扰文档内容:

  • 利用onSelectionChange触发器监听用户选区变化
  • 将用户ID、光标位置同步到共享Google Sheets作为数据中转
  • 其他用户通过定时触发器拉取最新位置,在对应位置插入隐藏评论作为标识
  • 自动清理过期标识,避免界面杂乱

代码实现

1. 选区变化监听脚本(绑定到目标Docs)

function onSelectionChange(e) {
  const doc = DocumentApp.getActiveDocument();
  const user = Session.getActiveUser().getEmail();
  const selection = e.selection;
  if (!selection) return;

  // 获取光标所在段落索引与偏移量
  const rangeElement = selection.getRangeElements()[0];
  const paragraph = rangeElement.getElement().asParagraph();
  const paraIndex = paragraph.getParent().getChildIndex(paragraph);
  const offset = rangeElement.getStartOffset();

  // 同步数据到共享Sheet(需提前创建Sheet并替换ID)
  const sheet = SpreadsheetApp.openById("你的共享Sheet ID").getActiveSheet();
  // 删除用户旧记录
  const rows = sheet.getDataRange().getValues();
  for (let i = rows.length - 1; i >= 0; i--) {
    if (rows[i][0] === user) sheet.deleteRow(i + 1);
  }
  // 插入新位置记录
  sheet.appendRow([user, doc.getId(), paraIndex, offset, Date.now()]);
}

2. 定时更新协作者标识脚本(绑定到目标Docs)

function updateCollaboratorCursors() {
  const doc = DocumentApp.getActiveDocument();
  const currentUser = Session.getActiveUser().getEmail();
  const sheet = SpreadsheetApp.openById("你的共享Sheet ID").getActiveSheet();
  const rows = sheet.getDataRange().getValues();

  // 清理旧标识(筛选带特定标签的评论)
  doc.getComments().forEach(comment => {
    if (comment.getAuthor().getEmail() !== currentUser && comment.getContent().includes("COLLAB_CURSOR")) {
      comment.remove();
    }
  });

  // 拉取并显示其他用户的有效位置(过滤5秒前的旧数据)
  const now = Date.now();
  rows.forEach(row => {
    const [user, docId, paraIndex, offset, timestamp] = row;
    if (user === currentUser || docId !== doc.getId() || (now - timestamp) > 5000) return;

    const paragraph = doc.getBody().getChild(paraIndex).asParagraph();
    const position = doc.newPosition(paragraph, offset);
    // 插入带用户标识的隐藏评论
    doc.addComment(position, `${user.split('@')[0]} 正在此处编辑 | COLLAB_CURSOR`);
  });
}

// 创建定时触发器(每2秒执行一次)
function createUpdateTrigger() {
  ScriptApp.newTrigger("updateCollaboratorCursors")
    .timeBased()
    .everySeconds(2)
    .create();
}

进阶方案(接近Slides实时指针效果)

通过自定义侧边栏+前端JS追踪浏览器鼠标坐标,生成悬浮彩色指针,更贴合Slides的原生体验:

  • 侧边栏JS监听文档区域的鼠标移动事件,将相对坐标同步到后端
  • 共享Sheet存储所有协作者的实时坐标与滚动位置
  • 其他用户的侧边栏定时拉取数据,在页面生成绝对定位的悬浮指针

代码实现

1. 侧边栏HTML(cursor-sidebar.html)

<!DOCTYPE html>
<html>
<body>
  <script>
    const docContainer = document.querySelector(".kix-appview-editor");
    let lastSyncTime = 0;

    // 监听鼠标移动并节流同步
    docContainer.addEventListener("mousemove", (e) => {
      const now = Date.now();
      if (now - lastSyncTime < 500) return;
      lastSyncTime = now;

      const rect = docContainer.getBoundingClientRect();
      // 计算相对文档区域的坐标
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const scrollTop = docContainer.scrollTop;
      // 同步到后端脚本
      google.script.run.saveCursorPosition(x, y, scrollTop);
    });

    // 定时拉取并渲染协作者指针
    setInterval(() => {
      google.script.run.withSuccessHandler(cursors => {
        // 清除旧指针
        document.querySelectorAll(".collab-cursor").forEach(el => el.remove());
        // 生成新指针
        cursors.forEach(cursor => {
          const pointer = document.createElement("div");
          pointer.className = "collab-cursor";
          pointer.style.cssText = `
            position: absolute; left: ${cursor.x}px; top: ${cursor.y + cursor.scrollTop}px;
            width: 12px; height: 12px; border-radius: 50%; pointer-events: none; z-index: 9999;
            background-color: ${getUserColor(cursor.user)};
          `;
          pointer.title = cursor.user.split('@')[0];
          docContainer.appendChild(pointer);
        });
      }).getCollaboratorCursors();
    }, 500);

    // 根据邮箱生成固定专属颜色
    function getUserColor(email) {
      let hash = 0;
      for (let i = 0; i < email.length; i++) {
        hash = email.charCodeAt(i) + ((hash << 5) - hash);
      }
      return `hsl(${hash % 360}, 70%, 50%)`;
    }
  </script>
</body>
</html>

2. 后端配套脚本(绑定到目标Docs)

// 打开协作者指针侧边栏
function showCursorSidebar() {
  const html = HtmlService.createHtmlOutputFromFile("cursor-sidebar.html")
    .setTitle("协作者指针");
  DocumentApp.getUi().showSidebar(html);
}

// 保存当前用户的鼠标位置
function saveCursorPosition(x, y, scrollTop) {
  const user = Session.getActiveUser().getEmail();
  const docId = DocumentApp.getActiveDocument().getId();
  const sheet = SpreadsheetApp.openById("你的共享Sheet ID").getActiveSheet();

  // 删除用户旧记录
  const rows = sheet.getDataRange().getValues();
  for (let i = rows.length - 1; i >= 0; i--) {
    if (rows[i][0] === user && rows[i][1] === docId) sheet.deleteRow(i + 1);
  }
  // 插入新位置数据
  sheet.appendRow([user, docId, x, y, scrollTop, Date.now()]);
}

// 获取其他协作者的有效位置数据
function getCollaboratorCursors() {
  const currentUser = Session.getActiveUser().getEmail();
  const docId = DocumentApp.getActiveDocument().getId();
  const sheet = SpreadsheetApp.openById("你的共享Sheet ID").getActiveSheet();
  const rows = sheet.getDataRange().getValues();
  const now = Date.now();
  const cursors = [];

  rows.forEach(row => {
    const [user, doc, x, y, scrollTop, timestamp] = row;
    if (user === currentUser || doc !== docId || (now - timestamp) > 1000) return;
    cursors.push({ user, x, y, scrollTop });
  });
  return cursors;
}

注意事项

  • 共享Sheet需给所有协作者开放编辑权限,作为位置数据的同步载体
  • 触发器执行频率不宜过高,避免触发Apps Script配额限制
  • 进阶方案需保持侧边栏打开才能生效,悬浮指针样式可能受Docs页面更新影响,需定期调整
  • 简化方案仅追踪文本光标位置,无法覆盖文档空白区域的鼠标移动

内容的提问来源于stack exchange,提问作者ChocolateChara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:44:58