视口高度为 0:一次浏览器自动化调试的完整复盘

起点:一次普通的自动化任务

任务本身没有技术含量:打开社区页面,点击按钮,提交表单。真正消耗时间的是第一步,点击工具直接报错:

Click coordinates (85, 31) are outside the visible viewport

坐标是正数,为什么会超出可视区域?

第一层:视口尺寸是 0x0

打印几个尺寸就能看出异常:

javascript 复制代码
window.innerWidth;                     // 0
window.innerHeight;                    // 0
document.documentElement.clientWidth;  // 0

视口为 0 时 CSS 布局照常完成,但 getBoundingClientRect() 的返回值整体落在负半轴:

javascript 复制代码
el.getBoundingClientRect();
// top: -480, left: -12, width: 84, height: 28

自动化工具习惯把 rect 的中心点当成真实鼠标坐标,负坐标被判定为视口外,点击直接被拒绝。结论很朴素:按钮不是找不到,是点不到。

第二层:绕开坐标,直接派发事件

在同一元素上合成事件序列可以绕过坐标层。组件库常常依赖 pointerdown 与 mousedown 切换内部状态,只派发 click 会静默失效,所以整套事件要补齐:

javascript 复制代码
function fire(el, x, y) {
  ['pointerover', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach(function (tp) {
    var E = tp.indexOf('pointer') === 0 ? PointerEvent : MouseEvent;
    el.dispatchEvent(new E(tp, { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y }));
  });
}

列表里按钮在视口外的情况,配合 scrollIntoView 一并解决:先把元素滚到中间,再取 rect 派发事件。

第三层:contenteditable 只认快捷键

评论框是一个 contenteditable 的 div。写入内容需要触发框架自己的输入监听:

javascript 复制代码
var el = document.querySelector('.rich-input');
el.focus();
el.textContent = 内容;
el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true }));

内容进了 DOM,字数统计也更新了,但点发送没有任何请求发出。这类富文本框的提交挂在键盘事件上,真正有效的是快捷键 Ctrl+Enter,换成真实按键后一次通过。

写入走 input 事件,提交走键盘事件,两步分开是这个坑的核心。

第四层:隐藏模板被误读成风控

页面上能读到「当前操作失败」的字样,第一反应是触发风控。检查可见性之后才发现那是常驻隐藏节点:

javascript 复制代码
var m = document.querySelector('.risk-modal');
var r = m.getBoundingClientRect();
// width 为 0 且 height 为 0:模板一直存在,从未弹出

innerText 与 textContent 都会包含隐藏节点。判断弹窗是否真的出现,必须用 getBoundingClientRect().width 大于 0 来断言,否则每一轮都会误判中止。

第五层:两步动作被当成一步

收藏按钮点了没反应,因为它只负责打开收藏集弹窗,真正的写入发生在第二步:

  1. 点收藏,弹出 .collection-list-modal;
  2. 勾选条目内的 label.checkbox-icon(点外层容器无效),再校验 input.checked 为 true;
  3. 点 .confirm-btn,此时才发出 POST /interact_api/v2/collectionset/add_article。

同类问题还有窄视口下的媒体查询:悬浮栏被 display none 隐藏,取到的 rect 全是 0,需要 style.setProperty 加 important 强制显形后再取坐标。

结论

这次调试没有一处卡在模型能力上,全部卡在运行环境与前端框架的契约上。可复用的做法是三条:

  • 先断言可见再动作:把 getBoundingClientRect().width 大于 0 作为所有点击前的守卫,隐藏元素直接跳过。
  • 一次动作一次校验:写完内容读字数,点完按钮读 class 或 URL,让失败稳定复现,而不是靠重试掩盖。
  • 以网络请求为终态证据:DOM 变化可能只是本地乐观更新,接口有没有发出才代表任务真正完成。

视口为 0 是极端环境,但这三条对生产环境的自动化脚本同样成立。点击失败往往不是元素不存在,而是脚本对「可见」和「成功」这两个判断过于乐观。

相关推荐
孔令飞2 小时前
很多人想学AI Infra,今天推荐一个 AI Agent / AI Infra 学习路线
云原生·go·ai编程
前端双越老师2 小时前
程序员 1v1 访谈系列第十一期:应届生别做开发
java·agent·ai编程
VIP_CQCRE2 小时前
在 VS Code 等编辑器里用 OpenCode 接入 Ace Data Cloud:Provider 配置与 IDE 验证
大模型·ai编程·vs code·opencode·ace data cloud
熊猫钓鱼>_>2 小时前
三方库 NDK 原生模块跨平台适配与调试【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙心迹
孟健16 小时前
我把9个AI助理迁到了Grok Bot,连远程服务器都省了
人工智能·aigc·ai编程
threerocks16 小时前
震撼首发 | 格物 · 蜡笔状态图 Skill,一句话,做一部会动的状态图
aigc·openai·ai编程
西安小哥17 小时前
大模型 Agent 替我操作浏览器跑了一遍社区日常任务:一份踩坑与工程记录
ai编程
文谦南宁AI普工17 小时前
主动性 Agent 的第一道工程题:Fast/Slow Path 两级过滤,附 80 行可跑模拟器
agent·ai编程·产品
用户1179104883317 小时前
AI输出满屏井号星号?hmharness把Markdown渲染成终端表格 GitHub:swsgbl/hmharness
ai编程·harmonyos