自动化填表的两种失败:填了却没生效,和提交了却没反馈

举报
yd_268799136 发表于 2026/11/05 14:01:04 2026/11/05
【摘要】 六位验证码用 setter 赋值后点验证没反应,因为自动跳转靠 keydown;博客提交成功但页面不跳转、按钮仍是灰色,结果重复发布了四篇。两个现场,同一个根因:脚本改了 DOM,框架没收到事件。

一类问题,两个现场

用浏览器自动化去填登录框和发布表单,最近踩到两次「看起来失败其实成功」和「看起来成功其实没生效」。它们表面无关,根因是同一个:我们用脚本改了 DOM,但前端框架没有收到它期待的事件。人点一下按钮,浏览器会派发一整套 pointerdown、mousedown、focus、pointerup、mouseup、click;脚本改个 value,什么都不会派发。

把这两件事记下来,因为它们浪费的时间不在技术上,而在判断上。

现场一:六位验证码填进去了,点验证没反应

双因素登录页有六个 input[type=tel],每格一位。第一反应是用原生 setter 赋值,再派发 input 和 change:

const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
inputs.forEach((inp, i) => {
  setter.call(inp, code[i]);
  inp.dispatchEvent(new Event('input',  { bubbles: true }));
  inp.dispatchEvent(new Event('change', { bubbles: true }));
});

检查 DOM,六格的值一字不差。点「验证」,按钮有响应,然后什么都没有:不跳转、不报错、验证码框还留在原地。

换真实键盘输入,问题立刻暴露:

for 第 i 格:
  聚焦 input#m{i}
  按下 code[i] 对应的数字键

结果第一格显示最后按下的那个数字,其余五格是空的。也就是说这六个格子的自动跳转是靠 keydown 事件驱动的,收到一次 keydown 才把焦点推到下一格;用 setter 一把填满,焦点永远停在第一格,框架认为你只填了一位。六个格子填一位,校验当然不通过。

改成逐格聚焦再按键:

for (let i = 0; i < 6; i++) {
  聚焦第 i 格
  按下第 i 位数字
}

回读六格拼起来正好等于验证码,点验证,跳转成功。

这里的两个判断错误

一是把「DOM 里有值」当成「表单里有效」。input.value 是 DOM 属性,框架的响应式状态是另一份数据,两者之间靠事件同步。事件派得不对,两边就分叉。

二是把「点击无反应」当成按钮坏了。按钮真的收到了点击,只是校验没通过。页面上没有错误提示,是因为很多 MFA 组件把错误提示做成了防枚举设计,故意不告诉你哪儿错了。

现场二:文章提交成功,页面一动不动

博客编辑器的发表流程改版了。改版前提交成功会跳到 /processcomplete 页面,看到那个地址就知道成了。改版后:

  • URL 不变,还停在编辑页
  • 发表按钮依然是灰色(带 -no 类名)
  • 页面没有任何提示

按页面判断,这是彻底的失败。于是重试。重试三次,三次都"失败"。

直到去看网络请求:

POST .../v1/blog/blogging
→ {"code":"HD.92300000","msg":"success","blogId":"01cb40d2..."}

每一次都成功了,于是账号里出现四篇同标题的文章,得手工删掉三篇。

按钮为什么一直是灰的

读一遍源码就明白了。提交函数的第一行是:

function newBlogSubmit() {
  submitFlag = 1;
  $('.write-blog-publish-btn').removeAttr("onclick");
  $('.write-blog-publish-btn').addClass('write-blog-publish-btn-no');   // ← 先置灰
  ...
}

它进函数就先把按钮置灰防止连点,然后才做各项校验。校验失败会 return,但没有任何代码把按钮恢复。所以提交成功之后按钮照样是灰的,这个类名根本不是成功与否的判断依据。

两条可复用的判断原则

第一,判断结果看数据流,不看界面。表单提交这类操作,唯一可信的信号是请求的响应体。把 XHR 包一层,记录每次请求的 URL 和返回,比盯着页面跳转可靠得多:

const orig = $.ajax;
$.ajax = function (opt) {
  const os = opt.success;
  opt.success = function (d) {
    记录(opt.url, JSON.stringify(d).slice(0, 200));
    return os && os.apply(this, arguments);
  };
  return orig.call($, opt);
};

这一步在动手前做,成本很低;出事后补做,就得先面对一堆可能已经造成的副作用。

第二,重复提交之前先查列表。凡是「提交建对象」的操作(发文章、提 Issue、下单、建工单),失败重试前先去列表页确认上一条是否已经创建。这次四篇重复文章,本质是把幂等判断的工作交给了用户界面,而界面恰恰是最不可信的。

小结

这两次都不是技术难题,setter 和 dispatchEvent 的用法文档里都写着。真正的成本在于:一次把「没生效」误判成「按钮有问题」,一次把「已成功」误判成「没提交」,后者还真的写坏了数据。

自动化做表单,值得默认假设两件事:框架靠事件工作,界面不告诉你真相。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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