top:50%+transform:translateY(-50%)是唯一靠谱方案,因其脱离文本流、不依赖font-size/line-height、兼容IE11+;父容器需显式设height,图标须用label包裹并设pointer-events:none,padding-right须精确计算。

为什么 top:50% + transform:translateY(-50%) 是唯一靠谱方案
因为密码输入框(<input type="password">)的行高、字体度量、内边距在不同浏览器(尤其是 iOS Safari 和 Android Chrome)中差异极大,用 line-height 或 vertical-align 会偏移,用 flex 套一层又容易破坏原生 focus 行为。而 top: 50% 把图标上边缘拉到父容器垂直中线,再用 transform: translateY(-50%) 向上挪动自身高度一半——这个计算完全脱离文本流,不依赖 font-size 或 line-height,兼容 IE11+ 且稳。
父容器必须显式设 height,不能靠 padding 帽子撑
很多人把 position: relative 直接加在 <input> 上,结果图标飘走。原因:<input> 是替换元素,它的“高度”由 font-size、padding、border 共同决定,但没有 layout height 属性可读;绝对定位的子元素参考的是父容器的 content box 高度,若父容器没设 height,计算就不可靠。
- 正确做法是包一层
<div class="input-wrapper">,给它设 <code>height: calc(1.5em + 12px)(对应font-size: 1em+padding: 6px) - 不要用
min-height或height: auto,它们在定位上下文中无法提供确定基准 - 如果用了 rem 单位,确保根字号稳定,避免缩放时失准
-
<label></label>必须包裹图标,并通过for属性关联<input id="pwd"> - 图标本身要加
pointer-events: none,否则会拦截 label 的 click 透传(尤其 SVG 图标默认有 pointer-events:auto) - 如果用 React,别写死
htmlFor="pwd",要用动态htmlFor={inputId},防止多实例冲突 - 图标用
<svg width="16" height="16"></svg>?那至少留padding-right: 24px(16px 宽 + 8px 右侧间距) - 图标用
em单位(如width: 1em),则padding-right也得用em,比如padding-right: calc(1em + 0.5em) - 千万别只写
padding-right: 20px就完事——等换图标尺寸或缩放时,问题立刻暴露
图标点击聚焦 input 的两个硬性条件
直接给图标绑 onclick="input.focus()" 在 Safari 移动端大概率失效——浏览器阻止非表单控件主动触发 focus。必须走 HTML 原生机制:
padding-right 留白必须等于图标宽度 + 间距
这是最常漏的一步。即使图标视觉居中了,文字仍可能被遮挡或光标卡在图标后面。留白不是估的,得算:
立即学习“前端免费学习笔记(深入)”;
真正难的不是让图标“看起来居中”,而是让它在各种缩放、字体加载、iOS zoom、暗色模式切换下都不偏不崩。所有看似细节的约束(height 显式、pointer-events、padding 计算),其实都是为了对抗浏览器渲染的不确定性。


















