前端JavaScript编写鼠标事件的完整指南

 更新时间:2026年05月30日 09:14:03   作者:detayun  
鼠标事件是Web交互的基础,点击、拖拽、滚轮、悬停几乎所有操作都离不开它,本文从基础到实战,讲清楚每个事件怎么用,什么时候用,有哪些坑

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

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

一、八个核心事件对比

事件触发时机典型用途
click完整点击(按下+松开)按钮、链接、提交
dblclick双击打开文件、编辑文本
mousedown鼠标按下瞬间拖拽开始、按住触发
mouseup鼠标松开瞬间拖拽结束
mousemove鼠标移动实时跟踪位置、绘图
mouseenter鼠标进入元素悬停显示提示
mouseleave鼠标离开元素隐藏提示
wheel滚轮滚动切换图片、缩放
contextmenu右键点击自定义右键菜单

最常用的四个:clickmousedownmousemovewheel

二、基础写法

// 方式1:addEventListener(推荐)
document.getElementById('btn').addEventListener('click', function(e) {
    console.log('点击了');
});
// 方式2:on事件属性(简单场景可用)
<button onclick="alert('点击了')">点我</button>
// 方式3:内联事件(不推荐,维护性差)
<div onmouseover="this.style.background='red'">悬停变红</div>

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

三、事件对象e里有什么

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:拖拽滑块旋转图片

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

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:滚轮切换图片

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:拖拽浮动框

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

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 更常用。

// ❌ 不推荐:进入子元素会反复触发
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()

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

解决:在 mousedownwheel 里加 e.preventDefault()

坑2:mousemove性能问题

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

解决:用 requestAnimationFrame 节流。

var ticking = false;
document.addEventListener('mousemove', function(e) {
    if (!ticking) {
        requestAnimationFrame(function() {
            // 在这里写业务逻辑
            ticking = false;
        });
        ticking = true;
    }
});

坑3:拖拽时元素被选中

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

解决:CSS 全局禁用选中。

.sliding_block {
    user-select: none;
    -webkit-user-select: none;
}

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

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

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

解决:mousemovemouseup 绑在 document 上,不是绑在滑块上。

// ✅ 正确
box.onmousedown = function() { ... };
document.onmousemove = function() { ... };  // 绑在 document 上
document.onmouseup = function() { ... };    // 绑在 document 上

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

坑5:用onclick绑定多个事件

// ❌ 后面的会覆盖前面的
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不冒泡,不会反复触发
第一行判断是否在输入框避免打字时误触发

总结

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

  1. 点击用 click,拖拽用 mousedown + mousemove + mouseup
  2. 滚轮用 wheel,记得 preventDefault() + { passive: false }
  3. mousemove 绑在 document 上,不是绑在元素上
  4. 第一行判断是否在输入框中

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

到此这篇关于前端JavaScript编写鼠标事件的完整指南的文章就介绍到这了,更多相关JavaScript鼠标事件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js以对象为索引的关联数组

    js以对象为索引的关联数组

    在代码逻辑中更多的是用关联数组的方式。但即使是这样我们也很少使用对象类型作为键值对的键名。
    2010-07-07
  • Javascript 获取滚动条位置等信息的函数

    Javascript 获取滚动条位置等信息的函数

    有时为了准确定位一个元素,我们需要获取滚动条的位置,这种需求经常出现在 tooltip 和 拖放等应用中,其实这个技术很简单,关键是要考虑浏览器的兼容性,本文就是介绍这一问题的解决方法。
    2009-09-09
  • 小程序实现页面多级来回切换的示例代码

    小程序实现页面多级来回切换的示例代码

    这篇文章主要为大家详细介绍了小程序如何页面多级来回切换支持滑动和点击操作,文中的实现步骤讲解详细,快跟随小编一起动手尝试一下吧
    2022-07-07
  • JS实现向iframe中表单传值的方法

    JS实现向iframe中表单传值的方法

    这篇文章主要介绍了JS实现向iframe中表单传值的方法,涉及js针对页面元素及表单属性操作相关实现技巧,需要的朋友可以参考下
    2017-03-03
  • 各情景下元素宽高的获取实现代码

    各情景下元素宽高的获取实现代码

    为了叙述简单,这里仅拿width示例。获取元素的offsetWidth/offsetHeight,减去元素的padding和border。
    2011-09-09
  • 使用javascript实现判断当前浏览器

    使用javascript实现判断当前浏览器

    这篇文章主要介绍了使用javascript实现判断当前浏览器的类型及版本,虽然不是很全面,但是还是推荐给大家,简单学下方法和思路。
    2015-04-04
  • js ajaxfileupload.js上传报错的解决方法

    js ajaxfileupload.js上传报错的解决方法

    这篇文章主要为大家详细介绍了js ajaxupload.js上传报错的解决方法,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • js实现省市级联效果分享

    js实现省市级联效果分享

    这篇文章主要为大家详细介绍了js实现省市级联效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • javascript 关闭IE6、IE7

    javascript 关闭IE6、IE7

    通过javascript关闭浏览器的时候,“一般”会弹出系统的默认提示框,提示用户是否关闭浏览器,这可以通过javascript解决,但是IE6,IE7解决的办法是不一样的
    2009-06-06
  • extjs 04_grid 单击事件新发现

    extjs 04_grid 单击事件新发现

    EXTJS GRID 中单击行和单元格获得行或者单元格的内容(数据),本文将整理此功能的应用,需要了解的朋友可以参考下
    2012-11-11

最新评论