鼠标与滚轮事件
鼠标事件是web开发中最常用的一类事件,毕竟鼠标是最主要的定位设备
DOM3级事件中定义了9个鼠标事件:
- click:在用户单击主鼠标按钮(一般为鼠标左键)或者按下回车时触发,这一点对确保易访问性十分重要,这意味着 onclick 事件处理程序既可以通过键盘执行也可以通过鼠标执行
- dbclick:在用户双击主鼠标按钮时触发(鼠标左键)。从技术上说该事件并不是DOM2级规范所支持的,但是由于浏览器对其的广泛实现在DOM3级规范中将其纳入标准
- mousedown:用户按下任意鼠标按钮触发,不能通过键盘触发
- mouseenter:鼠标光标从元素外部移入元素内部时触发,该事件不冒泡,并且在光标从元素移入元素子元素时不会触发
- mouseleave:光标从元素上方移出元素范围时触发,该事件不冒泡,移入后代元素不会触发
- mousemove:鼠标指针在元素内部移动时重复触发,不能通过键盘触发该事件
- mouseout:鼠标位于元素上方,此时将鼠标移至另一个元素触发,其子元素也会触发,不能通过键盘触发该事件
- mouseover:鼠标位于元素外部,首次移入元素范围内触发,其子元素也会触发,不能通过键盘触发该事件
- mouseup:用户释放鼠标按钮时触发,不能通过键盘触发
需要注意的是,页面上所有元素都支持鼠标事件。除了mouseenter和mouseleave事件,其余的鼠标事件都会冒泡,也可以被取消,取消鼠标事件会影响浏览器的默认行为
只有在同一个元素上相继触发 mousedown 和 mouseup 事件才会触发click 事件,若两者中有一者被取消,那么就不会触发click事件。
类似地,只有触发两次 click 事件才会触发一次dbclick事件(并且两次地间隔在一定时间范围内),若中途被代码取消则不会触发该事件
以上四个事件的触发顺序如下:
- mousedown
- mouseup
- click
- mousedown
- mouseup
- click
- dbclick
显然 click、dbclick都依赖于其它先行事件的触发,而 mousedown 和 mouseup则不受其它事件的影响
检测浏览器是否支持以上事件可使用以下代码:
var isSupported = document.implementation.hasFeature("MouseEvent","3.0");
鼠标事件中还有一类滚轮事件,说是一类事件实际上就是 mousewheel 事件。该事件跟踪鼠标滚轮,以及Mac的触控面板等相似设备
客户区坐标位置
鼠标事件都是在浏览器视口的特定位置发生的
该位置信息保存在鼠标事件对象的 clientX 和 clientY 属性中
他们的值分别表示事件触发时鼠标指针在浏览器窗口中的水平坐标和垂直坐标
注意该坐标不包括浏览的的工具栏等,也就是说该坐标是浏览器可视窗口内部的坐标(相对于浏览器窗口而非文档,也就是并不表示该元素在页面中的位置)
如:
var div = document.getElementByTagName("div")[0]; div.onclick = function(event){// 此处没有对 event 对象进行兼容处理,详见之前的文章 alert("Client coordinates:"+ event.clientX +","+event.clientY); }
页面坐标位置
通过客户区坐标可以知道鼠标事件在视口中的发生位置
而页面坐标的信息则保存在 事件对象的 pageX 和 pageY 属性中
该属性可以告知我们是事件是在页面中的什么位置发生的
因此该属性是以页面本身的左边和顶边作为参照计算的
var div = document.getElementByTagName("div")[0]; div.onclick = function(event){// 此处没有对 event 对象进行兼容处理,详见之前的文章 alert("Client coordinates:"+ event.pageX +","+event.pageY); }
在页面没有经过滚动的情况下,页面坐标和客户区坐标是相等的
IE8及更低版本的IE不支持事件对象上的页面坐标的属性
不过我们可以通过计算的方式来获取
代码如下:
var div = document.getElementByTagName("div")[0]; div.onclick = function(event){// 此处没有对 event 对象进行兼容处理,详见之前的文章 var pageX = event.pageX; var pageY = event.pageY; if(pageX === undefined){ pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if(pageY===undefined){ pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } alert("Page coordinates:"+pageX+","+pageY); }
屏幕坐标位置
鼠标事件发生时,不仅会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置
而相对于电脑屏幕的位置信息保存在事件对象的 screenX 和 screenY 属性上
代码如下:
var div = document.getElementByTagName("div")[0]; div.onclick = function(event){// 此处没有对 event 对象进行兼容处理,详见之前的文章 alert("Screen coordinates:"+ event.screenX+","+event.screenY); }
修改键
虽然鼠标事件主要是由鼠标来触发的,但在按下鼠标时键盘上某些键的状态也可以影响到索要采取的操作
这些键为:
- shift
- ctrl
- alt
- Meta(windows是win键苹果是CMD键)
他们经常被用于修改鼠标事件的行为
DOM为此规定了四个属性,用于表示这些修改键的状态
- shiftKey
- ctrlKey
- altKey
- metaKey
这四个属性都保存着布尔值,以表示鼠标事件触发时这些修改键的状态(true 表示按下 false 表示没有按下)
通过检测这些事件属性,我们可以更加灵活地使用鼠标事件
相关元素
在发生 mouseover 和 mouseout 事件时,除了触发元素外还会涉及更多元素
因为这两个事件都涉及从一个元素的边界之内移入另一个元素的边界之内
对mouseover 事件而言事件的主目标是 获得光标的元素 而相关元素就是那个失去光标的元素
类似的,对于mouseout 事件而言 失去光标的是主目标,而相关元素则是获得光标的元素
DOM 通过 event 对象的 relatedTarget 属性提供相关元素的信息
该属性只对 mouseout 和 mouseover 事件存在,其它事件该属性的值为 null
IE8以下不支持该属性但提供了类似的实现
兼容处理如下:
function eventFunction(e){ var events; if(e.relatedTarget){ events.relatedTarget = e.relatedTarget; }else if(e.toElement){ events.relatedTarget = e.toElement; }else if(e.fromElement){ events.relatedTarget = e.fromElement; }else{ events.relatedTarget = null; }// 相关元素兼容 }
鼠标按钮
只有在主鼠标按钮被单击的时候才会触发click
因此检测按钮的信息不是必要的
但是对于 mousedown 和 mouseup 事件来说用户点击的是鼠标的哪一个按钮是一个比较重要的信息
所以 在其 event 对象存在一个 button 属性,表示按下或者释放的按钮
DOM中的 button 属性可能有以下3个值:
- 0 表示主鼠标按钮
- 1 表示中间鼠标按钮
- 2 表示次鼠标按钮
在常规设置中,主鼠标按钮一般是指鼠标左键 中鼠标按钮指鼠标滚轮,次鼠标按钮则指的是鼠标右键
需要注意的是在低版本IE中的button与DOM标准中的 button 属性存在很大差异:
- 0:没有按下按钮
- 1:按下鼠标主按钮
- 2:按下鼠标次按钮
- 3:同时按下主次按钮
- 4:按下鼠标中键按钮
- 5:同时按下主按钮和中间按钮
- 6:同时按下中间按钮和次按钮
- 7:同时按下三个按钮
两者差异的兼容可通过以下代码实现:
function getButton(event){ if(document.implementation.hasFeature("MouseEvents","2.0")){ return event.button; }else{ switch(event.button){ case 0: case 1: case 3: case 5: case 7: return 0; case 2: case 6: return 2; case 4: return 1; } } }
更多事件信息
”DOM2级事件“规范在 event 对象中还提供了 detail 属性,用于给出有关事件的更多信息
对于鼠标事件来说,detail 包含了一个数值,表示在给定位置上发生了多少次单击
在同一个元素上相继发生一次mousedown 和 mouseup 算作发生一次单击
detail属性从1开始计数,每次单击发生后都会递增,若在mousedown和mouseup之间鼠标移动了位置则 detail 会被重置为0
鼠标滚轮事件
鼠标滚轮事件由IE6.0首先实现,此后其它浏览器也相继实现了该事件
与 mousewheel 事件对应的 event 对象除了包含鼠标事件的所有标准信息之外还包含一个特殊的 wheelDelta 属性
当用户向前(远离自己)的方向滚动滚轮时该值是120的倍数
当用户向后(靠近自己)的方向滚动滚轮时该值是-120的倍数
需要注意的是在 opera 9.5版本之前,正负号是反的
除了 opera 之外作妖的还有火狐
火狐支持一个名为 DOMMouseScroll 的类似事件,该事件也在鼠标滚轮滚动时触发
与DOM不同的地方在于,该事件的 wheelDelta的相关对应信息保存在 detail 属性中
向前滚动时该值是-3的倍数
向后滚动时是3的倍数
该事件会冒泡到window
对于滚轮事件的跨浏览器粗略实现如下:
function getWheelDelta(e){ var clients = getClient();//该方法用于获取浏览器的特性信息,详见之前的文章 https://www.cnblogs.com/lhyxq/p/10227375.html if(e.wheelDelta){ events.wheelDelta = (clients.engine.opera && clients.engine.opera < 9.5 ? -e.wheelDelta:e.wheelDelta) }else{ return -e.detail * 40; }// 滚轮事件兼容 }
触摸设备
IOS和Android设备实现非常特别,因为这些设备没有鼠标
需要注意以下几点:
- 不支持dbclick,双击会放大画面且无法修改该行为
- 轻击可单击元素会触发mousemove事件,该操作导致内容变化则不再发生其它事件,若没有变化则会依次发生 mousedown mouseup click 事件
- mousemove 会触发 mouseover 和 mouseout
- 两个手指放在屏幕上滑动页面会mousewheel事件
无障碍问题
若web应用需要确保残疾人或者使用屏幕阅读器的人能够访问,使用鼠标事件就需要特别小心
因此不建议使用 click 之外的其他鼠标事件来展示功能或者引发代码执行
需注意以下三点:
- 不要为了加快速度而使用mousedown事件代替 click 事件,因为在屏幕阅读器无法触发该事件
- 不要使用 mouseover 向用户展示新选项,原因同上,无法触发
- 不要使用 dbclick 执行重要操作,因为键盘无法触发该事件
以上可以提升web应用对残疾人的易访问性