浏览器自动化踩坑:一个「点不动」的登录按钮,背后是两套 UI 共存
现象
用 Playwright 驱动一个登录页,流程很标准:切到「密码登录」标签页、fill 账号密码、点击「登录」。输入框里能看到值,但点击之后没有任何请求发出,URL 也没变。
第一反应是表单校验没过:改成逐字键盘输入、手动触发 blur、甚至把按钮的 disabled 状态强制改掉,依旧没反应。
排查
先把页面上所有「登录」文本节点列出来,顺带看可见性:
Array.from(document.querySelectorAll('*'))
.filter(e => e.innerText?.trim() === '登录')
.map(e => ({ id: e.id, cls: e.className, vis: e.offsetParent !== null }))
结果有两个:一个属于带 ng-click 的旧版 AngularJS 按钮(#loginBtn),另一个属于新版组件的 div.hwid-btn-primary。再看尺寸:
document.querySelector('#loginBtn').getBoundingClientRect() // width: 0, height: 0
旧按钮是隐藏的。按文本或 CSS 定位并取第一个匹配时,命中的正好是这个不可见节点,点击自然落不到真实按钮上。
原因
站点在做 UI 改版,新旧两套登录表单同时留在 DOM 里,靠样式切换显示。旧表单挂着 AngularJS 的 ng-click,新表单用原生事件绑定。对真人没影响,但对「按文本定位 + 取第一个」的自动化脚本是个坑。
两个容易误判的信号:document.body.innerText 会把两套 UI 的文字都带出来,看起来「都在」;旧按钮的 disabled 由旧 scope 控制,改它并不能让新按钮工作。
解法
1. 先过滤可见性再定位。用一个稳定函数判断,而不是只看元素存在:
const visible = e => {
const r = e.getBoundingClientRect();
return e.offsetParent !== null && r.width > 0 && r.height > 0;
};
// Playwright 里可直接用 :visible
await page.locator('.hwid-btn:has-text("登录"):visible').click();
2. 用结构特征而不是纯文本。优先用语义化 id、埋点属性(如 ht)或组件 class 定位,文本只作为辅助校验。
3. 用网络请求验证动作真的发生。点击后别只看「命令没报错」,监听有没有发出预期的 POST、有没有跳转授权页。没请求就等于没点到。
小结
表单自动化里「元素找到了」不等于「元素可交互」。遇到点击无反应,按这个顺序查最快:① 元素是否可见;② 是否命中同名的隐藏副本;③ 是否被弹窗或遮罩拦截(Playwright 的报错会给出拦截节点);④ 点击后是否产生预期请求。把可见性判断固化成工具函数,比每次临时调选择器省事得多。
- 点赞
- 收藏
- 关注作者
评论(0)