浏览器自动化填表单的三个坑:悬浮遮罩、动态下拉与提交按钮定位
把每天在云声平台提交一条产品建议这件事做成自动化,本来以为只是"填三个输入框 + 点提交",实际跑下来连续踩了四个坑。这篇记录的是排查过程本身,方法不依赖具体工具,任何用 DOM 驱动表单的场景都能复用。
坑一:下拉菜单"存在但不可见"
表单里的"关联产品/功能"是必填项,用的是 tagator 这类标签插件。第一次排查时我用选择器查到了全部候选项,列表完整,以为可以直接点,结果点击始终落空。
原因很简单:候选项从一开始就在 DOM 里,只是父容器 .tagator_options 的 display 是 none。插件在每次输入时重新渲染并控制显隐,DOM 存在性完全不代表可交互。
判定可见性不要用"能不能查到节点",要用几何信息:
const el = document.querySelector('.target-option');
const r = el.getBoundingClientRect();
const visible = r.width > 0 && r.height > 0;
第二个发现是展开时机。真实鼠标点击输入框并不会展开这个下拉,插件监听的是键盘事件。需要先聚焦,再手动派发一次 keyup:
const input = document.querySelector('.tagator_input');
input.focus();
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(
new KeyboardEvent('keyup', { key: 'k', keyCode: 75, bubbles: true })
);
验证方式是立刻回读父容器的 getComputedStyle(...).display,确认从 none 变成 block,再继续点击。不要跳过这一步直接点,否则失败信息只会告诉你"元素找不到",很容易误判成选择器写错。
坑二:绝对定位的悬停面板挡住了点击目标
标签选完,下一步勾免责声明,点击时报"目标被遮挡"。但从可访问性树上看,那个勾选框明明在页面上、坐标也算得出来。
罪魁祸首是一个悬停触发的大面板:.more-block-cover.show-more-block,position: absolute、z-index: 7,宽度铺满整行、高度约 60px,展开后正好压在下面的免责声明区域上。它没有背景色,肉眼几乎看不出来,点下去却全被它吃掉。
定位遮挡元素的思路是反向查:拿到目标中心坐标,问浏览器这个点最上层是谁。
const box = document.getElementById('agreementBox');
box.scrollIntoView({ block: 'center' });
const r = box.getBoundingClientRect();
const x = r.left + r.width / 2, y = r.top + r.height / 2;
console.log(document.elementFromPoint(x, y));
如果打印出来不是目标元素,而是一个陌生的容器,那基本就是遮挡。顺着它的父链看 position 和 z-index,能快速确认是谁盖住了谁。
关掉悬停面板的方式是补一个离开事件,而不是去改样式:
const block = document.querySelector('.tag-block');
['mouseout', 'mouseleave'].forEach(type =>
block.dispatchEvent(
new MouseEvent(type, { bubbles: true, relatedTarget: document.body })
)
);
事件派发后回读面板的 class 和 display,确认 show-more-block 已被移除,再执行点击,一次就过。
坑三:提交按钮被容器包裹,点击落空
表单最后一关是提交。按文本匹配"提交建议",命中了一个 div.submit-block,点下去没有任何反应,也没有报错。
打印它的结构才发现,真正绑定事件的是里面那个 100% 宽的按钮元素,外面这层 div 只是个布局容器。点击坐标落在容器的边距上,自然什么都不会发生:
<div class="submit-block btn-block">
<span id="submit-btn" class="btn btn-primary">提交建议</span>
</div>
经验是:按文本定位到元素后,先判断它是不是叶子节点。如果不是,继续往下找第一个可点击的子元素,或者直接优先用 id、cursor: pointer 这类强特征做选择器。
坑四:接口报错,其实已经提交成功
最麻烦的不是失败,而是"看起来失败"。第一次提交后页面弹出"数据开小差,请稍后再试",表单内容全都还在,于是我判定失败并重试了一次。
结果去个人中心的"我的建议"列表一看,第一条建议已经创建成功,时间是刚才那一刻,状态"预审中"。所谓报错,是服务端对重复提交的拒绝,被前端统一渲染成了泛化错误文案。
这类场景必须建立"以业务状态为准"的校验:提交动作之后,不要相信页面提示,去列表页或详情页确认记录是否真实存在。判断成功要满足其一——列表条数增加、出现唯一标题、状态字段可读,任一成立即视为成功并立即停止重试。
对需要每日执行的任务,这一点尤其重要:重复提交不但浪费额度,还可能触发风控。
可复用的排查清单
踩完这四个坑后,我把表单自动化的检查顺序固化成了一份清单:
1. 每个交互前,用几何尺寸判断目标是否真的可见,而不是只看节点是否存在;
2. 点击失败先查遮挡,用坐标反查最上层元素,顺着父链找 z-index 和 position;
3. 按文本定位后,确认命中的是可点击元素本身,必要时下沉一层;
4. 提交类操作以服务端业务状态为唯一成功判据,失败重试前先查一次列表;
5. 每步操作后回读关键字段的实际值(标题长度、正文字符数、勾选态 class),确认写入生效再走下一步。
小结
浏览器自动化的难点从来不在"点"这个动作,而在页面把一个真实用户会遇到的交互拆成了多少层:显隐控制、悬停层、容器包裹、异步反馈。把每一层都当成独立的验证点,失败信息就不会再含糊,脚本也能从"大部分时候能跑"变成"失败时知道为什么"。
- 点赞
- 收藏
- 关注作者
评论(0)