Star 按钮点错了:隐藏副本 DOM 与可见 DOM 的状态分裂
一次自动化操作里,同一个 Star 按钮我点了两次:第一次页面上的文字变成了 Starred,可仓库的星标数没动;第二次才真正生效。两次点击的目标元素 class 完全一样,区别只在一个数字:getBoundingClientRect().width,一次是 25,一次是 0。
现象
脚本按 .status-btn__text 取元素,用 find() 拿到第一个文本含 Star 的节点,派发 pointerdown / mousedown / pointerup / mouseup / click 五连事件。返回之后回读 textContent,变成 Starred,看起来成功。可页面主区域仍然写着 Star 118,星标数没有任何变化。
原因
这个页面的头部渲染了两份操作区:一份给宽屏,一份给窄屏,都用 repo-action-group--star 包裹,内部按钮的结构相同、class 相同。宽屏那份被 CSS 隐藏,但它仍然留在 DOM 里,所以 querySelector 能选中它。
在被隐藏的副本上派发事件,前端框架照样改了它的本地状态,这就是文本变 Starred 的来源;而提交网络请求的那层逻辑挂在可见节点上,或者被隐藏容器的 display:none 短路了,请求根本没发出去。
判定方法
选中元素之后先算一次矩形,宽高为 0 的直接丢弃,再做文本匹配:
const visible = [...document.querySelectorAll(".status-btn__text")]
.filter(el => el.getBoundingClientRect().width > 0);
这样拿到的一定是真正占据布局的那个节点,派发事件才会走到提交逻辑。点完再回读一次宽高与文本,两个都变了才算走通。
校验要看服务端渲染的量
只回读 textContent 判断点击是否成功并不可靠,它反映的是本地状态,不等于服务端已受理。稳的校验是回读服务端渲染的计数:星标数、点赞数、收藏数、评论数,数字变了才算数。
还有一条辅助线索是 class:成功之后节点会从 status-btn is-no-wrapper 变成 status-btn starred is-no-wrapper。class 变了但计数没变,就是本地状态改了、请求没走,正好对应隐藏副本这种情况。
小结
同类问题不止出现在星标按钮上。凡是响应式布局会给同一功能渲染多份节点的页面,包括移动端与桌面端各一份、折叠区里一份、弹层里一份,用选择器批量取第一个都可能踩到隐藏副本。三个动作可以固定下来:取节点先过滤宽高,派发事件后回读 class,最后用服务端计数收口。
- 点赞
- 收藏
- 关注作者
评论(0)