HTML如何写鼠标事件

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

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


一、八个核心事件对比

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

最常用的四个:clickmousedownmousemovewheel


二、基础写法

javascript 复制代码
// 方式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 里有什么

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()

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

解决:在 mousedownwheel 里加 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 绑在滑块上,结果拖出滑块就失效了。

解决:mousemovemouseup 绑在 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 不冒泡,不会反复触发
第一行判断是否在输入框 避免打字时误触发

总结

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

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

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

相关推荐
a1117761 小时前
网页我的世界游戏 MC (html 开源)
游戏·开源·html
AI砖家1 小时前
前端 JavaScript 异步处理全方案详解:从回调到 Observable
开发语言·前端·javascript
用户713874229002 小时前
构建现代 Web 应用的令牌安全体系:Refresh Token Rotation、HttpOnly Cookie 与 Grace Period 全解析
前端
之歆2 小时前
Day21_电商详情页核心技术实战:从LESS预处理到复杂交互实现
开发语言·前端·javascript·css·交互·less
海鸥两三2 小时前
基于 Vue 3 + 高德地图的网格规划系统实战(有源码)
前端·javascript·vue.js
丷丩2 小时前
MapLibre GL JS第11课:获取鼠标指针坐标
前端·javascript·gis·地图·mapbox·maplibre gl js
代码AI弗森2 小时前
前端周刊第 467 期[特殊字符] 本期精选目录
前端
随便的名字2 小时前
前端路由的底层逻辑:URL 中 # 和 ? 的区别与关系详解
前端
kongba0072 小时前
ttyd Web终端安装指南(OpenCloudOS 9)
linux·前端