鼠标事件是Web交互的基础。点击、拖拽、滚轮、悬停……几乎所有操作都离不开它。但很多人只会写onclick其实鼠标事件远不止这些。本文从基础到实战讲清楚每个事件怎么用、什么时候用、有哪些坑。一、八个核心事件对比事件触发时机典型用途click完整点击按下松开按钮、链接、提交dblclick双击打开文件、编辑文本mousedown鼠标按下瞬间拖拽开始、按住触发mouseup鼠标松开瞬间拖拽结束mousemove鼠标移动实时跟踪位置、绘图mouseenter鼠标进入元素悬停显示提示mouseleave鼠标离开元素隐藏提示wheel滚轮滚动切换图片、缩放contextmenu右键点击自定义右键菜单最常用的四个click、mousedown、mousemove、wheel。二、基础写法// 方式1addEventListener推荐document.getElementById(btn).addEventListener(click,function(e){console.log(点击了);});// 方式2on事件属性简单场景可用button onclickalert(点击了)点我/button// 方式3内联事件不推荐维护性差div onmouseoverthis.style.backgroundred悬停变红/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/YvspageX/Y怎么选clientX/Y→ 相对于浏览器窗口不受滚动影响pageX/Y→ 相对于整个文档包含滚动距离做拖拽、定位用clientX/Y更方便。四、实战验证码识别页面的鼠标交互场景1拖拽滑块旋转图片这是我们项目中的核心交互——按住滑块左右拖动图片同步旋转。varboxdocument.getElementsByClassName(sliding_block)[0];varisDropfalse;varangle0;// 按下开始拖拽box.onmousedownfunction(e){varee||window.event;varxe.clientX-box.offsetLeft;varye.clientY-box.offsetTop;isDroptrue;};// 移动实时更新位置和角度document.onmousemovefunction(e){if(!isDrop)return;varee||window.event;varmoveXe.clientX-x;// 限制范围 0~360varminX0,maxX360;if(moveXminX)moveXminX;if(moveXmaxX)moveXmaxX;box.style.leftmoveXpx;anglemoveX;$(#baidu_img).attr(style,transform: rotate(angledeg););$(#msg_angle_span).html(angle);};// 松开结束拖拽document.onmouseupfunction(){isDropfalse;};关键点mousedown记录初始位置mousemove实时计算偏移mouseup结束拖拽三个事件必须配合使用缺一不可场景2滚轮切换图片document.addEventListener(wheel,function(e){// 避免在输入框中触发if(e.target.tagNameINPUT||e.target.tagNameTEXTAREA){return;}e.preventDefault();// 必须加否则页面会跟着滚if(e.deltaY0){next_img();// 滚轮向下 → 下一张}else{last_img();// 滚轮向上 → 上一张}},{passive:false});// passive: false 是关键e.deltaY的含义deltaY 0→ 滚轮向下deltaY 0→ 滚轮向上deltaX→ 水平滚动横向滚轮场景3拖拽浮动框项目中还有个浮动框可以自由拖动varfloatingDivdocument.getElementById(floatingDiv);varinitialXnull,initialYnull;floatingDiv.addEventListener(mousedown,function(e){initialXe.clientX-floatingDiv.getBoundingClientRect().left;initialYe.clientY-floatingDiv.getBoundingClientRect().top;e.preventDefault();// 防止选中文本});document.addEventListener(mousemove,function(e){if(initialXnull)return;vardeltaXe.clientX-initialX;vardeltaYe.clientY-initialY;floatingDiv.style.leftdeltaXpx;floatingDiv.style.topdeltaYpx;});document.addEventListener(mouseup,function(){initialXnull;initialYnull;});五、mouseenter vs mouseover 的区别事件冒泡触发时机推荐场景mouseover✅ 冒泡进入子元素也会触发需要事件冒泡时mouseenter❌ 不冒泡只在进入自身时触发悬停效果优先用这个同理mouseleavevsmouseout也是mouseleave更常用。// ❌ 不推荐进入子元素会反复触发div.addEventListener(mouseover,function(){this.style.backgroundyellow;});// ✅ 推荐只在进入/离开 div 时触发一次div.addEventListener(mouseenter,function(){this.style.backgroundyellow;});div.addEventListener(mouseleave,function(){this.style.background;});六、五个常见坑坑1忘了e.preventDefault()右键会弹出浏览器菜单滚轮会滚动页面拖拽会选中文本。解决在mousedown或wheel里加e.preventDefault()。坑2mousemove性能问题mousemove触发频率极高每秒几十次里面写重逻辑会卡顿。解决用requestAnimationFrame节流。vartickingfalse;document.addEventListener(mousemove,function(e){if(!ticking){requestAnimationFrame(function(){// 在这里写业务逻辑tickingfalse;});tickingtrue;}});坑3拖拽时元素被选中拖拽过程中文字被蓝框选中体验很差。解决CSS 全局禁用选中。.sliding_block{user-select:none;-webkit-user-select:none;}你的代码里已经加了做得很好 ✅坑4事件绑定在错误的元素上mousemove绑在滑块上结果拖出滑块就失效了。解决mousemove和mouseup绑在document上不是绑在滑块上。// ✅ 正确box.onmousedownfunction(){...};document.onmousemovefunction(){...};// 绑在 document 上document.onmouseupfunction(){...};// 绑在 document 上// ❌ 错误box.onmousemovefunction(){...};// 鼠标移出滑块就失效坑5用onclick绑定多个事件// ❌ 后面的会覆盖前面的btn.onclickfunction(){alert(1);};btn.onclickfunction(){alert(2);};// 只会弹出 2// ✅ 用 addEventListener 可以绑定多个btn.addEventListener(click,function(){alert(1);});btn.addEventListener(click,function(){alert(2);});// 两个都会执行七、鼠标事件设计原则原则说明拖拽用mousedownmousemovemouseup三件套缺一不可mousemove绑在document上防止拖出元素失效滚轮用wheelpreventDefault{ passive: false }必须加悬停用mouseenter/mouseleave不冒泡不会反复触发第一行判断是否在输入框避免打字时误触发总结鼠标事件的核心就四句话点击用click拖拽用mousedownmousemovemouseup滚轮用wheel记得preventDefault(){ passive: false }mousemove绑在document上不是绑在元素上第一行判断是否在输入框中掌握这四点你就能搞定90%的鼠标交互需求。
HTML如何写鼠标事件
鼠标事件是Web交互的基础。点击、拖拽、滚轮、悬停……几乎所有操作都离不开它。但很多人只会写onclick其实鼠标事件远不止这些。本文从基础到实战讲清楚每个事件怎么用、什么时候用、有哪些坑。一、八个核心事件对比事件触发时机典型用途click完整点击按下松开按钮、链接、提交dblclick双击打开文件、编辑文本mousedown鼠标按下瞬间拖拽开始、按住触发mouseup鼠标松开瞬间拖拽结束mousemove鼠标移动实时跟踪位置、绘图mouseenter鼠标进入元素悬停显示提示mouseleave鼠标离开元素隐藏提示wheel滚轮滚动切换图片、缩放contextmenu右键点击自定义右键菜单最常用的四个click、mousedown、mousemove、wheel。二、基础写法// 方式1addEventListener推荐document.getElementById(btn).addEventListener(click,function(e){console.log(点击了);});// 方式2on事件属性简单场景可用button onclickalert(点击了)点我/button// 方式3内联事件不推荐维护性差div onmouseoverthis.style.backgroundred悬停变红/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/YvspageX/Y怎么选clientX/Y→ 相对于浏览器窗口不受滚动影响pageX/Y→ 相对于整个文档包含滚动距离做拖拽、定位用clientX/Y更方便。四、实战验证码识别页面的鼠标交互场景1拖拽滑块旋转图片这是我们项目中的核心交互——按住滑块左右拖动图片同步旋转。varboxdocument.getElementsByClassName(sliding_block)[0];varisDropfalse;varangle0;// 按下开始拖拽box.onmousedownfunction(e){varee||window.event;varxe.clientX-box.offsetLeft;varye.clientY-box.offsetTop;isDroptrue;};// 移动实时更新位置和角度document.onmousemovefunction(e){if(!isDrop)return;varee||window.event;varmoveXe.clientX-x;// 限制范围 0~360varminX0,maxX360;if(moveXminX)moveXminX;if(moveXmaxX)moveXmaxX;box.style.leftmoveXpx;anglemoveX;$(#baidu_img).attr(style,transform: rotate(angledeg););$(#msg_angle_span).html(angle);};// 松开结束拖拽document.onmouseupfunction(){isDropfalse;};关键点mousedown记录初始位置mousemove实时计算偏移mouseup结束拖拽三个事件必须配合使用缺一不可场景2滚轮切换图片document.addEventListener(wheel,function(e){// 避免在输入框中触发if(e.target.tagNameINPUT||e.target.tagNameTEXTAREA){return;}e.preventDefault();// 必须加否则页面会跟着滚if(e.deltaY0){next_img();// 滚轮向下 → 下一张}else{last_img();// 滚轮向上 → 上一张}},{passive:false});// passive: false 是关键e.deltaY的含义deltaY 0→ 滚轮向下deltaY 0→ 滚轮向上deltaX→ 水平滚动横向滚轮场景3拖拽浮动框项目中还有个浮动框可以自由拖动varfloatingDivdocument.getElementById(floatingDiv);varinitialXnull,initialYnull;floatingDiv.addEventListener(mousedown,function(e){initialXe.clientX-floatingDiv.getBoundingClientRect().left;initialYe.clientY-floatingDiv.getBoundingClientRect().top;e.preventDefault();// 防止选中文本});document.addEventListener(mousemove,function(e){if(initialXnull)return;vardeltaXe.clientX-initialX;vardeltaYe.clientY-initialY;floatingDiv.style.leftdeltaXpx;floatingDiv.style.topdeltaYpx;});document.addEventListener(mouseup,function(){initialXnull;initialYnull;});五、mouseenter vs mouseover 的区别事件冒泡触发时机推荐场景mouseover✅ 冒泡进入子元素也会触发需要事件冒泡时mouseenter❌ 不冒泡只在进入自身时触发悬停效果优先用这个同理mouseleavevsmouseout也是mouseleave更常用。// ❌ 不推荐进入子元素会反复触发div.addEventListener(mouseover,function(){this.style.backgroundyellow;});// ✅ 推荐只在进入/离开 div 时触发一次div.addEventListener(mouseenter,function(){this.style.backgroundyellow;});div.addEventListener(mouseleave,function(){this.style.background;});六、五个常见坑坑1忘了e.preventDefault()右键会弹出浏览器菜单滚轮会滚动页面拖拽会选中文本。解决在mousedown或wheel里加e.preventDefault()。坑2mousemove性能问题mousemove触发频率极高每秒几十次里面写重逻辑会卡顿。解决用requestAnimationFrame节流。vartickingfalse;document.addEventListener(mousemove,function(e){if(!ticking){requestAnimationFrame(function(){// 在这里写业务逻辑tickingfalse;});tickingtrue;}});坑3拖拽时元素被选中拖拽过程中文字被蓝框选中体验很差。解决CSS 全局禁用选中。.sliding_block{user-select:none;-webkit-user-select:none;}你的代码里已经加了做得很好 ✅坑4事件绑定在错误的元素上mousemove绑在滑块上结果拖出滑块就失效了。解决mousemove和mouseup绑在document上不是绑在滑块上。// ✅ 正确box.onmousedownfunction(){...};document.onmousemovefunction(){...};// 绑在 document 上document.onmouseupfunction(){...};// 绑在 document 上// ❌ 错误box.onmousemovefunction(){...};// 鼠标移出滑块就失效坑5用onclick绑定多个事件// ❌ 后面的会覆盖前面的btn.onclickfunction(){alert(1);};btn.onclickfunction(){alert(2);};// 只会弹出 2// ✅ 用 addEventListener 可以绑定多个btn.addEventListener(click,function(){alert(1);});btn.addEventListener(click,function(){alert(2);});// 两个都会执行七、鼠标事件设计原则原则说明拖拽用mousedownmousemovemouseup三件套缺一不可mousemove绑在document上防止拖出元素失效滚轮用wheelpreventDefault{ passive: false }必须加悬停用mouseenter/mouseleave不冒泡不会反复触发第一行判断是否在输入框避免打字时误触发总结鼠标事件的核心就四句话点击用click拖拽用mousedownmousemovemouseup滚轮用wheel记得preventDefault(){ passive: false }mousemove绑在document上不是绑在元素上第一行判断是否在输入框中掌握这四点你就能搞定90%的鼠标交互需求。