浏览器自动化的七个坑:从「点不动」到「点了没反应」

举报
yd_296806354 发表于 2026/11/12 22:19:00 2026/11/12
【摘要】 填表、点按钮、翻页,流程不难。难的是每一步都可能「看起来成功了,其实没生效」。七个真实踩过的坑,以及一个三步调试顺序。

最近用浏览器自动化跑一批固定流程。流程本身不难——填表、点按钮、翻页。难的是每一步都可能「看起来成功了,其实没生效」。

这篇记录七个实际踩到的坑。它们不属于任何框架的文档,属于"你得自己撞一次才知道"的类型。

坑一:坐标点击对某些组件无效

常规的点击是往某个坐标派发一次 click。但很多组件库监听的不是 click,而是更靠前的指针事件。

一个 Vue 生态的下拉选择器,正确的事件序列是:

pointerdown → mousedown → pointerup → mouseup → click

只发 click,下拉会弹出、选项会高亮,但选中值不写回。表现是:你以为选好了,提交时报「请选择」。

这类组件的内部状态机通常是这样:pointerdown 时记录候选,pointerup 时判定「在同一元素上释放」才提交。跳过头一个事件,状态机进不去。

判断方法:如果点击后 UI 有反应(高亮/展开)但数据没变,几乎一定是事件序列不全。

坑二:提交按钮可能不是你看到的那个元素

页面上有个「报名」按钮,视觉上是一个 <div class="btn-block confirm">。点它,什么都没发生。

看 DOM 才发现:

<div class="btn-block confirm">
  <a id="sub" class="btn btn-red" href="javascript:void(0);">报名</a>
</div>

真正的 handler 绑在里面的 <a> 上,外层的 div 只是个布局容器。坐标落在 div 的 padding 区域时,点击事件不会被内层元素收到。

教训:点之前先用 elementFromPoint(x, y) 确认那个坐标上到底是什么。

坑三:复选框不是 input

需要勾选一个协议。document.querySelectorAll('input[type=checkbox]') 找到 7 个,全是 width: 0——它们是被隐藏的真实 input,视觉上的勾选框是一个空的 <span>,点击靠 CSS + JS 自己实现。

<span id="agreementBox" mate_data_ts="....click.免责声明"></span>

对隐藏 input 调 .click() 会改 checked 属性,但框架不读它——它读的是自己的状态。要直接点那个 span。

配套现象:按钮一直是 disabled 的灰色状态,直到 span 的 class 变成 checked。

坑四:编辑器在 iframe 里,且只渲染视口

富文本编辑器(比如 TinyMCE)在一个跨域 iframe 里。直接 querySelector 拿不到内容,得走它自己的 API:

window.tinymce.activeEditor.setContent(html);
window.tinymce.activeEditor.fire("change");  // 少了这句,框架认为没改过
window.tinymce.activeEditor.save();          // 少了这句,提交的是空内容

fire("change") 和 save() 是最容易漏的两步:内容确实进去了,但表单状态还是「未修改」,提交后是空白。

更隐蔽的是另一类编辑器(CodeMirror 6):它只渲染视口内的行。所以

document.querySelector(".cm-content").innerText.length

拿到的是可见部分的长度,不是全文。我用它校验「正文是否完整粘贴」,得到的结论是「少了 60% 内容」——实际上内容完整,只是屏幕外没渲染。

正确做法:滚到顶部看开头、滚到底部看结尾,两端对上即可。或者直接读状态:cmView.view.state.doc.length。

坑五:窗口最小化时,所有坐标都是假的

浏览器窗口最小化后:

window.innerWidth  → 0
window.innerHeight → 0

此时所有 getBoundingClientRect() 返回的都是相对一个 0×0 视口的坐标。点 (1376, 199) 会落到别的地方,或者落到 (0,0)。

而且不会报错。脚本一路"成功"执行完,截图时才抛一句「无法在窗口最小化时截图」。

教训:操作前先断言 window.innerWidth > 0。这是个一行成本的前置检查,能省掉一整轮排查。

坑六:响应式布局会让选择器集体失效

窗口宽度为 0 时,响应式样式会把卡片列表换成另一套 DOM 结构。同一个页面,宽度 1524 时有 10 个 .listBox,宽度 0 时有 0 个。

所以「选择器找不到元素」有时不是元素不存在,是布局分支不同。加上窗口最小化这个前提,就会得到一个很误导的结论:脚本昨天还能跑,今天所有 querySelectorAll 返回空。

坑七:hover 是前置依赖

有些按钮在 hover 时才挂载真正的点击逻辑(比如「Star」按钮:鼠标移入后展开成一个带图标的状态按钮)。直接 click 会命中还没初始化完的元素。

正确顺序:

await mouse.move(x, y);      // 先 hover
await sleep(400);            // 等组件展开
await mouse.click(x, y);     // 再点

一个通用的调试顺序

踩完这七个坑,我现在的排查顺序固定成三步:

  1. 先验证环境:窗口尺寸、页面是否是最新的、登录态是否有效
  2. 再验证元素:elementFromPoint 确认坐标上是什么;getBoundingClientRect 的宽高是否非零
  3. 最后验证状态:不要相信「点过了」,去读结果——按钮 class 变了吗?列表里多了一条吗?接口返回了什么?

第三条最重要。前面六个坑的共同点是:动作成功,状态未变。自动化脚本最容易犯的错,就是把「我执行了动作」当成「事情发生了」。

检查状态永远比检查动作可靠。这也是为什么我后来在每个关键步骤后面都加一行断言——读一次页面状态,比回看十行日志有用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。