直接改 input.type 是最可靠方案,但需同步处理光标、焦点、ARIA 和 Safari 兼容性;必须用 input.type = "text" 而非 setAttribute,且要缓存 value、手动恢复 selectionRange 和 focus,按钮需 aria-pressed、keydown 支持及动态 aria-label。

直接改 input.type 是最可靠、最轻量的方案,但必须同步处理光标、焦点、ARIA 和 Safari 兼容性问题,否则用户一点击就光标跳头、读屏器报错、旧版 Safari 崩掉输入内容。
为什么不能用 setAttribute("type", "text")
Safari 12–14 在调用 setAttribute 切换 type 时会触发渲染异常:输入框视觉卡顿、value 被清空、甚至整个表单失焦。必须用属性赋值语法 input.type = "text" 才能绕过该 bug。这不是“推荐”,而是 Safari 事实上的兼容性要求。
- 错误写法:
input.setAttribute("type", "text") - 正确写法:
input.type = "text"(或input.type = "password") - 切之前务必先缓存
input.value——某些 Android WebView 在 type 变更瞬间仍会丢值,哪怕你刚读过
光标和焦点必须手动恢复
仅改 type 会导致 Safari/Chrome 都把光标重置到开头,且焦点丢失。用户想继续编辑中间某位字符,结果光标跑到了最左,体验断裂。
- 切换前记录:
const start = input.selectionStart; const end = input.selectionEnd; - 切换后立即执行:
input.focus(); input.setSelectionRange(start, end); - 别漏掉
input.focus()——否则 Tab 键会跳过这个输入框,违反可访问性规范
按钮的 ARIA 和键盘支持不能只靠文字
仅靠按钮文字从“显示密码”变成“隐藏密码”,对屏幕阅读器无效。视障用户听到的仍是旧提示,且无法用空格/Enter 触发,等于功能残缺。
- 按钮必须带
aria-pressed="false"(初始状态),切换时同步更新为"true"或"false" - 必须监听
keydown事件,响应Enter和Space键,不能只绑click -
aria-label要动态更新,例如:btn.setAttribute("aria-label", isText ? "隐藏密码" : "显示密码"); - 图标切换别用
img.src === "eye-open.svg"判断——浏览器返回的是绝对 URL,永远不等;应改用 class 名或 data-state 属性
fallback 方案只在探测失败时启用
不是所有环境都支持 type 切换。必须先探测:input.type = "text"; const isSupported = input.type === "text"; input.type = "password";。若探测失败,才降级到 CSS 遮罩方案(如伪元素 + opacity + font-family 干扰),而非默认 fallback。
- 探测必须在页面加载后立即执行,不能等到用户点按钮才做
- fallback 方案需额外维护 selection 同步逻辑,复杂度陡增,只应在
isSupported === false时激活 - 禁用自动填充:
input.autocomplete = "off",否则 Chrome 在type="text"时可能拒绝唤起密码管理器
真正难的不是切换 type,而是让每一次切换都像没切换过一样——光标在哪还在哪,焦点没丢,读屏器知道状态变了,键盘操作照常生效。这些细节漏掉任意一个,就是生产环境里的真实 bug。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











