input:focus + .icon 无效是因为 CSS 相邻兄弟选择器 + 只能选 input 后面紧邻的 .icon,不能反向选择前面的元素;改用 :focus-within 或 JS 监听 focus/blur 切换父容器类名可解决。

input:focus + .icon 的选择器为什么无效
因为 .icon 是输入框的兄弟元素(比如放在 <div> 容器里),但 DOM 结构通常是 <input> 在前、<i class="icon"></i> 在后,而 CSS 的 + 只能选「紧邻后面」的兄弟,不能反向选前面的元素。所以写成 input:focus + .icon 永远不生效——除非你把图标 HTML 放在 input 后面,且是直接相邻。
用 :focus-within 绕过 DOM 顺序限制
只要把 input 和 .icon 包进同一个父容器,对父容器用 :focus-within 就能捕获子元素获得焦点的状态,不受前后顺序影响。
常见结构示例:
<div class="input-group"> <i class="icon"></i> <input type="text" placeholder="请输入"> </div>
对应 CSS:
立即学习“前端免费学习笔记(深入)”;
.input-group .icon {
opacity: 0.5;
transition: opacity 0.2s;
}
.input-group:focus-within .icon {
opacity: 1;
}
注意点:
-
:focus-within在 Chrome 60+、Firefox 52+、Safari 15.4+ 支持,IE 完全不支持 - 如果 input 被设为
tabindex="-1"或禁用状态(disabled),不会触发:focus-within - 父容器不能设
overflow: hidden且内容溢出,否则某些浏览器可能误判焦点状态
用 JS 监听 focus/blur 手动控制类名
兼容性要求高(比如要支持 IE11)或逻辑更复杂时,JS 是最稳的选择。关键是别直接操作 style,而是切换 class,方便复用和维护。
实操建议:
- 给父容器加一个稳定 class,比如
input-with-icon - 监听
input的focus和blur事件 - 在事件回调里对父容器添加/移除
focused类,CSS 基于该类控制图标样式
简短示例:
const inputs = document.querySelectorAll('.input-with-icon input');
inputs.forEach(input => {
const group = input.closest('.input-with-icon');
input.addEventListener('focus', () => group.classList.add('focused'));
input.addEventListener('blur', () => group.classList.remove('focused'));
});
CSS 配套:
.input-with-icon .icon { opacity: 0.5; }
.input-with-icon.focused .icon { opacity: 1; }
图标变亮的几种实现方式对比
“变亮”不等于只能调 opacity,不同方案效果和性能差异明显:
-
opacity:最常用,但会连带影响子元素,且半透明图标的边缘可能发虚 -
filter: brightness(1.3):只作用于图标本身,但部分老浏览器不支持,且在 Safari 中可能有渲染闪烁 -
color或fill(对 SVG 图标):精准、高性能,推荐优先用,例如.icon svg { fill: #007bff; }和.focused .icon svg { fill: #0056b3; } - 避免用
box-shadow模拟发光,容易糊、耗性能,且响应式下难控制
真正要注意的是:如果图标是字体图标(如 Font Awesome),得用 color 控制;如果是 SVG 内联,优先用 fill;如果是 PNG,opacity 或 filter 是仅有的选择——但 PNG 图标在高 DPI 屏上容易模糊,这点常被忽略。


















