在网页交互里,鼠标悬停替换图片是很常见的需求,比如商品列表展示另一角度,或者头像悬停显示动图。很多初学者直接在onmouseover里改src,在onmouseout里又写死旧地址,结果动态更新后恢复出错。更稳妥的思路是用数据属性缓存初始地址,让恢复逻辑自动适配变化。

基本原理与属性设计
浏览器中每个图片元素都有默认的src属性指向当前显示资源。当我们需要悬停切换时,核心问题是离开后如何知道原来是什么。最简单且不易出错的办法,是在第一次加载或渲染时,把原始地址记录到元素的自定义属性上,例如data-origin。这样无论后续业务怎么改src,原始值都安静躺在属性里。
使用自定义属性而不是JS变量,好处是属性和DOM节点绑定,即使页面有多个图片、或是节点被框架重新创建,只要属性跟着走就不会丢。同时HTML5规范允许任意以data-开头的属性,不会影响页面合法性。下面代码展示如何在HTML中预设:
<img src="https://ipipp.com/a.jpg" data-hover="https://ipipp.com/a_hover.jpg" data-origin="https://ipipp.com/a.jpg" class="swap-img" /> <img src="https://ipipp.com/b.jpg" data-hover="https://ipipp.com/b_hover.jpg" data-origin="https://ipipp.com/b.jpg" class="swap-img" />
原生JavaScript事件绑定
我们可以统一给所有带swap-img类的图片绑定鼠标事件。在mouseenter时,把当前src换成data-hover的值;在mouseleave时,从data-origin读回原始地址并赋值。注意用mouseenter和mouseleave而不是mouseover和mouseout,前者不会因内部子元素冒泡重复触发,更适合图片这类无子节点的元素。
为了更智能,可以在切换前检查data-hover是否为空,避免无效请求。同时如果原始src在运行中被其他逻辑改过,我们应在每次恢复前重新同步data-origin,或者仅在首次加载时缓存一次,根据业务选择。以下脚本演示通用绑定:
document.querySelectorAll('.swap-img').forEach(function(img) {
if (!img.dataset.origin) {
img.dataset.origin = img.src;
}
img.addEventListener('mouseenter', function() {
var hover = img.dataset.hover;
if (hover) {
img.src = hover;
}
});
img.addEventListener('mouseleave', function() {
img.src = img.dataset.origin;
});
});
智能恢复与错误处理
所谓智能恢复,不仅是还原地址,还要应对悬停图加载失败的情况。如果data-hover对应的图404了,浏览器会触发error事件,此时若直接留在空图不好。我们可以在error里判断:如果当前src不是原始图,就马上切回data-origin,保证用户看到的始终是有效内容。
另外当页面用AJAX翻页,新插入的图片没有绑定事件,可以用事件委托在父容器上监听,或者每次渲染后重跑绑定函数。下面示例加上错误保护,提升健壮性:
document.querySelectorAll('.swap-img').forEach(function(img) {
if (!img.dataset.origin) {
img.dataset.origin = img.src;
}
img.addEventListener('mouseenter', function() {
var hover = img.dataset.hover;
if (hover && img.src !== hover) {
img.src = hover;
}
});
img.addEventListener('mouseleave', function() {
img.src = img.dataset.origin;
});
img.addEventListener('error', function() {
if (img.src !== img.dataset.origin) {
img.src = img.dataset.origin;
}
});
});
对比常见错误写法
有一种典型错误是在mouseout里写死旧地址,例如img.src = 'a.jpg'。当图片其实是b.jpg时,移出鼠标就显示错图。还有人用全局变量存上一张,但多图场景下变量被覆盖,恢复混乱。用data-origin则每个元素独立记忆,互不干扰。
另一个误区是频繁操作src不发请求控制,实际上浏览器会缓存图片,首次悬停后才会有网络消耗,之后流畅。若担心流量,可配合loading=lazy原生懒加载,或预加载 hover 图。下表列出两种方案差异:
| 方案 | 恢复准确性 | 多图支持 | 动态改图适配 |
|---|---|---|---|
| 写死原地址 | 低 | 差 | 不支持 |
| data-origin缓存 | 高 | 好 | 支持 |
总结与实践建议
用原生JavaScript做悬停切换并不复杂,关键是把原始资源地址通过data-origin固化在节点上,让恢复动作变成读取属性而非依赖记忆。再辅以error事件处理和合理的事件类型,就能覆盖大多数业务场景。对于使用框架的项目,原理相同,只是把属性绑在组件状态或ref上即可。
实际开发中建议把悬停图地址也由后端随列表返回,前端只负责读写data属性,这样运营改图不需要发版。若图片来自用户上传,注意对地址做编码,防止特殊字符破坏属性赋值。
JavaScript鼠标悬停图片切换修改时间:2026-08-08 15:03:31