直接修改 input.type 从 "password" 到 "text" 会导致 ios safari ≤15.3 清空值、丢失光标、明文暴露;可靠方案是双 input 切换或 inputmode+css 模拟,需兼顾光标同步、键盘适配与密码管理器兼容。

直接改 input.type 从 "password" 切到 "text" 看似最简,但会在 iOS Safari ≤15.3、部分 Android WebView 中清空值、丢失光标、暴露明文——这不是 bug,是规范行为。可靠方案只有两个:双 input 切换显隐,或用 inputmode + CSS 模拟明文观感。
为什么不能直接赋值 input.type = "text"
浏览器将 type 视为不可变属性。强行赋值后:
- iOS Safari ≤15.3 会清空
input.value,即使你立刻赋回去,selectionStart也归零 - DOM 中真实存在明文,DevTools、XSS、辅助技术可直接读取,违反最小暴露原则
- 屏幕阅读器仍播报“密码输入框”,但实际已是
type="text",语义断裂 - Chrome/Firefox 行为不一致:Firefox 保留值,Chrome 可能清空;且都不保证光标位置
双 input 元素切换(兼容 IE11 起)
在 DOM 中同时保有 <input type="password"> 和 <input type="text">,仅控制 visibility 显隐,完全规避 type 变更副作用。
- 两个
input共享同一name和id(仅一个设tabindex="0",另一个设tabindex="-1") - 用
visibility: hidden隐藏非活跃项,不用display: none(否则表单提交时字段丢失) - 同步值用
input事件监听,覆盖粘贴、拖入、IME 输入等所有变更路径 - 切换前记录
selectionStart/selectionEnd,切完立即focus()并setSelectionRange() - 按钮必须响应
keydown(Enter/Space),并动态更新aria-pressed和aria-label
用 inputmode + CSS 模拟明文(现代首选)
不碰 type,只改 inputmode 和视觉样式,让 type="password" 在用户感知上“像文本”。
- 设
input.inputmode = isShow ? "text" : "verbatim",避免 iOS 键盘错乱(如数字键盘误弹) - CSS 中用
.showing input { font-family: monospace; letter-spacing: 0.05em; }干扰掩码视觉,不依赖伪元素遮罩 - 禁用
autocomplete:切为明文时设autocomplete="off",防止密码管理器误填充 - 图标状态用
data-state="showing"控制,别用img.src字符串比对(返回的是绝对 URL) - 无障碍必须靠
aria-label动态更新,仅文字变化对屏幕阅读器无效
探测支持再 fallback,别默认降级
不要一上来就用 CSS 遮罩方案。先做运行时探测:
const input = document.getElementById("pwd");
const originalType = input.type;
input.type = "text";
const isSupported = input.type === "text";
input.type = originalType;
若 isSupported 为 false,再启用遮罩 fallback。探测必须在页面加载后立即执行,不能等到用户点击才判断——否则首次点击必然失败。
真正麻烦的不是切换逻辑,而是光标位置同步、键盘类型适配、密码管理器兼容这三件事。漏掉任意一个,用户在 iOS 上点一下就失焦,或输一半密码突然全变星号,体验直接崩掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











