最简方案是直接修改 input.type,但需兼容 IE 和旧 Safari:先探测是否支持(赋值后读回),不支持则降级为 CSS 遮罩;切换前保存 value、selectionRange,切换后恢复焦点与光标;同步更新 aria-pressed、inputmode 和图标状态(用 getAttribute("src") 或 CSS 类),并适配移动端键盘与密码管理器行为。

直接改 input.type 是最简方案,但 IE 和旧 Safari 会报错或静默失败
现代浏览器(Chrome/Firefox/Edge/Safari ≥14)都支持运行时修改 input.type,但 IE 完全不支持,部分旧版 Safari(≤13.1)在特定上下文中也会丢值或卡死。不能只写 input.type = "text" 就完事。
- 先做兼容性探测:赋值后立即读回
input.type,若仍为"password",说明不支持,需 fallback 到 CSS 遮罩方案 - 避免用
setAttribute("type", "text")—— Safari 旧版本有渲染 bug,必须用属性赋值语法 - 切换前手动保存
input.value,某些 WebView 在 type 变更瞬间会清空内容 - 禁用自动填充:
input.autocomplete = "off",否则 Chrome 在type="text"时可能拒绝触发密码管理器
togglePasswordVisibility() 必须同步处理光标、焦点和屏幕阅读器
只改 type 会导致光标跳到开头、焦点丢失、ARIA 状态错乱——用户按 Tab 会跳过输入框,视障用户听到的提示还是“显示密码”,实际已隐藏。
- 切换前记录
input.selectionStart和input.selectionEnd - 切换后立刻调用
input.focus()和input.setSelectionRange(start, end),否则 Safari/Chrome 都会重置光标 - 按钮必须设
aria-pressed="true/false",并随状态实时更新,不能只靠文字提示 - 按钮需监听
keydown事件响应 Enter/Space 键,仅绑click无法满足无障碍要求
图标切换别用 img.src === "eye.png" 直接比对
img.src 返回的是解析后的绝对 URL(如 https://site.com/eye.png),而 HTML 中写的是相对路径 "eye.png",两者永远不等。硬写字符串比较必然失效。
- 一律用
img.getAttribute("src")获取原始声明值,它返回的就是你写的"eye.png"或"eyec.png" - 推荐用 CSS 类控制图标(如
class="eye-icon eye-icon--closed"),比操作src更稳定、易维护 - 如果坚持用图片,确保两个文件名大小写完全一致,且路径在 Network 面板中能 200 加载成功
- SVG 图标更优:内联 SVG 不依赖外部资源,可直接用
fill或class控制状态,无加载失败风险
移动端软键盘和密码管理器行为要单独适配
type 切换在 iOS 和 Android 上会触发不同键盘类型,且密码管理器可能因 type="text" 拒绝填充或提示“非安全字段”。
立即学习“前端免费学习笔记(深入)”;
- 设置
input.inputmode = isShow ? "text" : "verbatim",避免数字键盘在明文模式下错误弹出 - 不要给明文态的密码框设
autocomplete="current-password",1Password / Bitwarden 会跳过该字段 - iOS Safari 在 type 切换后有时不会自动恢复密码键盘,可加
input.setAttribute("inputmode", "text")强制干预 - React/Vue 用户禁止用状态驱动重新渲染整个
input元素——这会导致输入法中断、光标丢失、v-model 脱钩
type,是让每一次点击都保持光标位置、不打断输入法、不惊动密码管理器、不骗过屏幕阅读器。这些细节一旦漏掉一个,用户就会在某个设备上卡住。



















