autocomplete=off 在 Chrome 里基本等于没写

三种常见抱怨,其实是同一件事:

  • 登录页的密码框被填成了注册时的密码;
  • 后台管理界面的输入框突然变成一片浅黄;
  • 花半小时做的输入框样式,用户一用自动填充就崩了。

Chrome 判断”这是什么字段”的依据

它看的不是 autocomplete="off",而是 name、id、type、placeholder、label 的文本、以及字段周边的文字。一旦判定为密码 / 地址 / 信用卡,off 会被直接忽略——这是既定行为,不是 bug,提 issue 也没用。

能动的是语义,不是开关:

<input type="password" name="new_pass" autocomplete="new-password">
<input type="password" name="confirm"  autocomplete="new-password">

注册 / 改密码场景用 new-password,登录场景用 current-password。两个密码框都要写,只写第一个不够——实测第二个仍会被填进旧密码。

浅黄背景的来路

那是 UA 的 :-webkit-autofill 伪类,浏览器用自己的背景色盖住你的 background。你不能直接改 background-color,只能”盖一层不透明的阴影”:

input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: #1f2328;              /* 文字色也得单独改 */
  -webkit-box-shadow: 0 0 0 1000px #fff inset;   /* 用阴影覆盖背景 */
  transition: background-color 9999s ease-in-out 0s; /* 老版本保险 */
}

0 0 0 1000px 的意思是一个 1000px 的 inset 阴影——足够大,能盖满整个输入框。标准伪类是 :autofill,Firefox 和 Safari 认,Chrome 现在也支持,两个都写更稳。

怎么知道用户用了自动填充

监听 input 事件不够可靠——自动填充的某些阶段不触发它。业界用了很多年的办法是借一个空动画:

@keyframes onAutoFillStart { from {} to {} }
input:-webkit-autofill { animation-name: onAutoFillStart; animation-duration: 1ms; }
document.addEventListener('animationstart', (e) => {
  if (e.animationName === 'onAutoFillStart') {
    console.log('被自动填充了:', e.target);
  }
}, true);   // 注意第三参数 true,事件在捕获阶段才能拿到

动画名命中就说明 UA 给它套上了 :-webkit-autofill。这个技巧不依赖任何私有 API,改版也难失效。

我的取舍

与其跟浏览器对抗,不如承认用户的数据已经在浏览器里了,把”被填充后的样子”也调好看。真正需要对抗的只有一个场景:注册页的密码框被填成了旧登录密码——那个用 new-password 就够了,不需要 off,也不需要 JS 清空。

顺便一提,那些”用 JS 定时清空输入框”的方案副作用都很大:用户自己输入到一半的内容也会被吞掉,还会让密码管理器反复弹窗。能不写就不写。

← 返回技术笔记