直接改 input.type 是唯一靠谱方案:现代浏览器稳定支持 type 切换,但须缓存恢复光标焦点、正确处理 ARIA 状态、动态更新 aria-label,并在 DOMContentLoaded 后立即探测兼容性。

直接改 input.type 是唯一靠谱方案
别折腾两个 input 同步、别用 CSS 伪元素盖层、更别监听 paste/cut 做“模拟明文”——这些都会导致光标错位、软键盘反复弹起、表单验证失效。现代浏览器(含 Safari 15+、Chrome 90+、Firefox 85+)已稳定支持直接赋值 input.type = "text" 或 input.type = "password",这是最轻量、最可靠、无障碍兼容性最好的方式。
但必须注意:不能用 input.setAttribute("type", "text"),Safari 12–14 会清空 value、失焦、甚至卡死;Android WebView 也有小概率丢值。所有兼容性问题都源于没按规范做状态缓存和恢复。
切换前必须缓存并恢复光标与焦点
只改 type 属性,浏览器会重置光标到开头、丢失焦点,用户正在编辑中间字符,切完就跳到最左,体验直接断裂。这不是“建议”,是必做项。
- 切换前读取:
const start = input.selectionStart;、const end = input.selectionEnd;、const wasFocused = document.activeElement === input; - 切换后立即执行:
input.focus();、input.setSelectionRange(start, end); - 漏掉
focus()会导致 Tab 键跳过该输入框,违反 WCAG 2.1 可访问性要求
按钮的 ARIA 和键盘支持不能省
仅靠图标变色或文字切换(如“显示密码”→“隐藏密码”),屏幕阅读器完全感知不到状态变化,也无法用空格/Enter 触发。视障用户会认为功能不可用。
立即学习“前端免费学习笔记(深入)”;
- 按钮初始必须带
aria-pressed="false",切换时同步更新为"true"或"false" - 必须监听
keydown,响应Enter和Space键,不能只绑click -
aria-label要动态更新,例如:btn.setAttribute("aria-label", isText ? "隐藏密码" : "显示密码"); - 图标切换逻辑应基于 class(如
fa-eye/fa-eye-slash),别用img.src字符串比对——浏览器返回的是绝对 URL,永远不等
兼容性探测必须在页面加载后立刻执行
不是所有环境都支持 type 切换(比如极老 WebView 或某些企业内嵌浏览器)。不能默认 fallback,得先探测再决定是否降级。
探测代码要写成:
const input = document.createElement('input');
input.type = 'password';
input.type = 'text';
const isSupported = input.type === 'text';
input.type = 'password';
如果 isSupported 为 false,才启用 CSS 遮罩方案(如伪元素 + font-family 干扰),否则一律走原生 type 切换。这个探测动作必须在 DOMContentLoaded 后立即执行,不能延迟到按钮点击时才做。
最容易被忽略的是:切换后没清空 autocomplete 缓存字段,或在表单提交前没强制切回 password 类型,导致明文意外出现在日志、监控或开发者工具中。



















