HTML如何写鼠标事件

8535 2026-07-17 06:48:44

鼠标事件是Web交互的基础。点击、拖拽、滚轮、悬停......几乎所有操作都离不开它。

但很多人只会写 onclick,其实鼠标事件远不止这些。本文从基础到实战,讲清楚每个事件怎么用、什么时候用、有哪些坑。

一、八个核心事件对比

事件

触发时机

典型用途

click

完整点击(按下+松开)

按钮、链接、提交

dblclick

双击

打开文件、编辑文本

mousedown

鼠标按下瞬间

拖拽开始、按住触发

mouseup

鼠标松开瞬间

拖拽结束

mousemove

鼠标移动

实时跟踪位置、绘图

mouseenter

鼠标进入元素

悬停显示提示

mouseleave

鼠标离开元素

隐藏提示

wheel

滚轮滚动

切换图片、缩放

contextmenu

右键点击

自定义右键菜单

最常用的四个:click、mousedown、mousemove、wheel。

二、基础写法

javascript

复制代码

// 方式1:addEventListener(推荐)

document.getElementById('btn').addEventListener('click', function(e) {

console.log('点击了');

});

// 方式2:on事件属性(简单场景可用)

// 方式3:内联事件(不推荐,维护性差)

悬停变红

结论:优先用 addEventListener,可以绑定多个事件,也方便解绑。

三、事件对象 e 里有什么

javascript

复制代码

element.addEventListener('click', function(e) {

console.log(e.clientX, e.clientY); // 鼠标相对于视口的坐标

console.log(e.pageX, e.pageY); // 鼠标相对于文档的坐标

console.log(e.target); // 触发事件的元素

console.log(e.button); // 哪个键:0左键,1中键,2右键

});

clientX/Y vs pageX/Y 怎么选?

clientX/Y → 相对于浏览器窗口,不受滚动影响

pageX/Y → 相对于整个文档,包含滚动距离

做拖拽、定位,用 clientX/Y 更方便。

四、实战:验证码识别页面的鼠标交互

场景1:拖拽滑块旋转图片

这是我们项目中的核心交互------按住滑块左右拖动,图片同步旋转。

javascript

复制代码

var box = document.getElementsByClassName("sliding_block")[0];

var isDrop = false;

var angle = 0;

// 按下:开始拖拽

box.onmousedown = function(e) {

var e = e || window.event;

var x = e.clientX - box.offsetLeft;

var y = e.clientY - box.offsetTop;

isDrop = true;

};

// 移动:实时更新位置和角度

document.onmousemove = function(e) {

if (!isDrop) return;

var e = e || window.event;

var moveX = e.clientX - x;

// 限制范围 0~360

var minX = 0, maxX = 360;

if (moveX < minX) moveX = minX;

if (moveX > maxX) moveX = maxX;

box.style.left = moveX + "px";

angle = moveX;

$("#baidu_img").attr("style", "transform: rotate(" + angle + "deg);");

$("#msg_angle_span").html(angle);

};

// 松开:结束拖拽

document.onmouseup = function() {

isDrop = false;

};

关键点:

mousedown 记录初始位置

mousemove 实时计算偏移

mouseup 结束拖拽

三个事件必须配合使用,缺一不可

场景2:滚轮切换图片

javascript

复制代码

document.addEventListener('wheel', function(e) {

// 避免在输入框中触发

if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {

return;

}

e.preventDefault(); // 必须加,否则页面会跟着滚

if (e.deltaY > 0) {

next_img(); // 滚轮向下 → 下一张

} else {

last_img(); // 滚轮向上 → 上一张

}

}, { passive: false }); // passive: false 是关键

e.deltaY 的含义:

deltaY > 0 → 滚轮向下

deltaY < 0 → 滚轮向上

deltaX → 水平滚动(横向滚轮)

场景3:拖拽浮动框

项目中还有个浮动框可以自由拖动:

javascript

复制代码

var floatingDiv = document.getElementById('floatingDiv');

var initialX = null, initialY = null;

floatingDiv.addEventListener('mousedown', function(e) {

initialX = e.clientX - floatingDiv.getBoundingClientRect().left;

initialY = e.clientY - floatingDiv.getBoundingClientRect().top;

e.preventDefault(); // 防止选中文本

});

document.addEventListener('mousemove', function(e) {

if (initialX === null) return;

var deltaX = e.clientX - initialX;

var deltaY = e.clientY - initialY;

floatingDiv.style.left = deltaX + 'px';

floatingDiv.style.top = deltaY + 'px';

});

document.addEventListener('mouseup', function() {

initialX = null;

initialY = null;

});

五、mouseenter vs mouseover 的区别

事件

冒泡

触发时机

推荐场景

mouseover

✅ 冒泡

进入子元素也会触发

需要事件冒泡时

mouseenter

❌ 不冒泡

只在进入自身时触发

悬停效果优先用这个

同理:mouseleave vs mouseout,也是 mouseleave 更常用。

javascript

复制代码

// ❌ 不推荐:进入子元素会反复触发

div.addEventListener('mouseover', function() {

this.style.background = 'yellow';

});

// ✅ 推荐:只在进入/离开 div 时触发一次

div.addEventListener('mouseenter', function() {

this.style.background = 'yellow';

});

div.addEventListener('mouseleave', function() {

this.style.background = '';

});

六、五个常见坑

坑1:忘了 e.preventDefault()

右键会弹出浏览器菜单,滚轮会滚动页面,拖拽会选中文本。

解决:在 mousedown 或 wheel 里加 e.preventDefault()。

坑2:mousemove 性能问题

mousemove 触发频率极高(每秒几十次),里面写重逻辑会卡顿。

解决:用 requestAnimationFrame 节流。

javascript

复制代码

var ticking = false;

document.addEventListener('mousemove', function(e) {

if (!ticking) {

requestAnimationFrame(function() {

// 在这里写业务逻辑

ticking = false;

});

ticking = true;

}

});

坑3:拖拽时元素被选中

拖拽过程中文字被蓝框选中,体验很差。

解决:CSS 全局禁用选中。

css

复制代码

.sliding_block {

user-select: none;

-webkit-user-select: none;

}

(你的代码里已经加了,做得很好 ✅)

坑4:事件绑定在错误的元素上

mousemove 绑在滑块上,结果拖出滑块就失效了。

解决:mousemove 和 mouseup 绑在 document 上,不是绑在滑块上。

javascript

复制代码

// ✅ 正确

box.onmousedown = function() { ... };

document.onmousemove = function() { ... }; // 绑在 document 上

document.onmouseup = function() { ... }; // 绑在 document 上

// ❌ 错误

box.onmousemove = function() { ... }; // 鼠标移出滑块就失效

坑5:用 onclick 绑定多个事件

javascript

复制代码

// ❌ 后面的会覆盖前面的

btn.onclick = function() { alert(1); };

btn.onclick = function() { alert(2); }; // 只会弹出 2

// ✅ 用 addEventListener 可以绑定多个

btn.addEventListener('click', function() { alert(1); });

btn.addEventListener('click', function() { alert(2); }); // 两个都会执行

七、鼠标事件设计原则

原则

说明

拖拽用 mousedown + mousemove + mouseup

三件套,缺一不可

mousemove 绑在 document 上

防止拖出元素失效

滚轮用 wheel + preventDefault

{ passive: false } 必须加

悬停用 mouseenter/mouseleave

不冒泡,不会反复触发

第一行判断是否在输入框

避免打字时误触发

总结

鼠标事件的核心就四句话:

点击用 click,拖拽用 mousedown + mousemove + mouseup

滚轮用 wheel,记得 preventDefault() + { passive: false }

mousemove 绑在 document 上,不是绑在元素上

第一行判断是否在输入框中

掌握这四点,你就能搞定90%的鼠标交互需求。

“暗影模式”昙花一现,别担心还会再次重逢的!
"小祖宗"是什么意思? -关于中文 (简体)(中文)