第十三章:事件类型(鼠标与滚轮事件)

来源:互联网 发布:c语言并查集简单例题 编辑:程序博客网 时间:2024/05/18 00:39

事件

事件类型

鼠标与滚轮事件

  • 鼠标事件是Web开发中最常用的一类事件。DOM 3级事件中定义了9个鼠标事件,简介如下:
    1. click:在用户单击主鼠标按钮(一般是左键)或者按下回车键触发。这意味着onclick事件处理程序既可以通过鼠标也可以通过键盘触发。
    2. dblclick:双击主鼠标按钮(一般是左键)时触发。这个事件不在DOM2级事件规范中,但DOM3级事件将其纳入了标准(我试过回车按两次,没有触发)。
    3. mousedown按下任意鼠标按钮时触发。不能通过键盘触发这个事件。
    4. mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。
    5. mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡不冒泡决定了“移动到后代元素上不会触发”的性质),在光标移动到后代元素上不会触发。该事件也是DOM3级事件才纳入标准的。
    6. mouseleave:在位于元素上方的鼠标光标移动到元素范围之外(非后代元素)时触发。这个事件不冒泡,该事件也是DOM3级事件才纳入标准的。
    7. mousemove:当鼠标指针在元素内部移动时重复触发。不能通过键盘触发这个事件。
    8. mouseout:这个事件和leave的区别是,移动到的元素可以是后代元素(因为该事件会冒泡)。
    9. mouseover:在鼠标指针位于一个元素外部,然后首次移入该元素内部时触发(和mouseenter的区别是移动到后代元素上也会触发,因为该事件会冒泡)。不能通过键盘触发这个事件。
  • 页面上的所有元素都支持鼠标事件。除了mouseenter和mouseleave,所有鼠标事件都会冒泡,也可以被取消,取消鼠标事件将会影响浏览器的默认行为。
  • 只有在同一个元素上相继触发mousedown和mouseup事件,才会重触发click事件,如果其中一个被取消,则不会触发click事件。连续触发两个click事件,会触发一次dblclick事件。这4个事件的触发顺序如下:
<!DOCTYPE html><html><head>    <meta charset="utf-8">    <title>Example</title></head><body>    <button id="btn">xxxx</button>    <script type="text/javascript">        function conlog(msg) {            return function() {                console.log(msg);            }        }        var btn = document.getElementById("btn");        btn.onmousedown = conlog("mousedown!");        btn.onmouseup = conlog("mouseup!");        btn.onclick = conlog("click!");        btn.ondblclick = conlog("dblclick!");    </script></body></html>------------------双击效果如下:Chrome IE9+mousedown!mouseup!click!mousedown!mouseup!click!dblclick!------------------双击效果如下:IE8- (使用古代浏览器要注意了)日志: mousedown! 日志: mouseup! 日志: click! 日志: mouseup! 日志: dblclick!
  • 使用下面的代码可以检测浏览器是否支持以上事件
    var isSupported = document.implementation.hasFeature("MouseEvents", "3.0");    //后一个参数填写"2.0"则不检测dblclick、mouseenter、mouseleave
  • 鼠标事件中还有一类滚轮事件。这一类事件,其实也就是一个mousewheel事件。这个事件跟踪鼠标滚轮,类似Mac的触控板。

客户区坐标位置

  • 鼠标事件都是在浏览器视口中的特定位置发生的。这个位置信息保存在事件对象clientXclientY属性中。所有浏览器都支持这两个属性,它们的值表示鼠标指针在视口的水平和垂直坐标。
<!DOCTYPE html><html><head>    <title>Client coordinates Example</title>    <script type="text/javascript" src="EventUtil.js"></script></head><body>    <div id="myDiv" style="background-color:red;height:100px;width:100px">Click me</div>    <script type="text/javascript">        var div = document.getElementById("myDiv");        div.onclick = function (event) {            alert("Client coordinates: " + event.clientX + "," + event.clientY);        }    </script></body></html>

页面坐标位置

  • 页面坐标位置使用事件对象pageX和pageY属性获取(IE8-没有)。这两个属性表示鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的(虽然结果可能是一样的)。
  • 在页面没有滚动的情况下,pageX和pageYclientXclientY的值相等

屏幕坐标位置

  • 和上面的类似,事件对象还有screenX和screenY两个表示相对于电脑屏幕坐标的属性。这两个属性IE8-也支持。例子省略。

修改键

  • 用过IDE写代码的都知道,比如要跳转到某个文件,我们可以在文件路径上按下ctrl键加左键跳转。在浏览器中也是类似。事件对象提供了以下4个Boolean类型的属性:
    1. shiftKey: true表示shift键按下
    2. ctrlKey
    3. altKey
    4. metaKey:在Windows中是Windows键,在Mac中是Cmd键 (IE8-不支持该属性)
<!DOCTYPE html><html><head>    <title>Modifier Keys Example</title>    <script type="text/javascript" src="EventUtil.js"></script></head><body>    <div id="myDiv" style="background-color:red;height:100px;width:100px">Click me while holding a modifier key</div>    <script type="text/javascript">        var div = document.getElementById("myDiv");        div.onclick = function(event){            var keys = new Array();            if (event.shiftKey){                keys.push("shift");                //通常在这个地方写入不一样的功能效果            }            if (event.ctrlKey){                keys.push("ctrl");            }            if (event.altKey){                keys.push("alt");            }            if (event.metaKey){                keys.push("meta");            }            alert("Keys: " + keys.join(","));        };     </script></body></html>

相关元素

  • 在发生mouseover和mouseout事件(mouseleave和mouseenter同理)时,还会涉及更多的元素。对mouseover而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素。对mouseout而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素。相关元素的引用通过event.relatedTarget获得。需要注意的是IE8-不支持该属性,得用fromElement和toElement代替。从名字上我们可以知道fromElement存的是mouseover的相关元素,而toElement存的是mouseout的相关元素。

鼠标按钮

  • event.button属性保存着按下或释放的鼠标按钮。对于mouseup和mousedown事件该属性有以下三个可能值:0表示主鼠标按钮(一般为左键);1表示中间按钮(滚轮);2表示次鼠标按钮(右键);
  • IE8-之前也提供了button属性,但这个属性和DOM的button属性差异很大:
    0:表示没有按下按钮。
    1:表示按下了主鼠标按钮。
    2:表示按下了次鼠标按钮。
    3:表示同时按下了主、次鼠标按钮。
    4:表示按下了中间的鼠标按钮。
    5:表示同时按下了主鼠标按钮和中间的鼠标按钮。
    6:表示同时按下了次鼠标按钮和中间的鼠标按钮。
    7:表示同时按下了三个鼠标按钮。

更多的事件信息

  • “DOM2级事件”规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。对于鼠标事件来说,detail包含了一个数值,表示在给定位置上发生了多少次单击。在同一个像素上相继地发生一次mousedown和一次mouseup事件算做一次单击。detail属性从1开始每次单击后会递增。如果移动了单击位置,那肯定会重新开始计算。另外如果单击一次后停顿时间够长(我测下来一两秒左右),detail就会重新开始计数。
  • IE 也通过下列属性为鼠标事件提供了更多信息。
    altLeft:布尔值,表示是否按下了Alt 键。如果altLeft 的值为true,则altKey 的值也为true。
    ctrlLeft:布尔值,表示是否按下了Ctrl 键。如果ctrlLeft 的值为true,则ctrlKey 的值也为true。
    offsetX:光标相对于目标元素边界的x 坐标。
    offsetY:光标相对于目标元素边界的y 坐标。
    shiftLeft:布尔值,表示是否按下了Shift 键。如果shiftLeft 的值为true,则shiftKey的值也为true。
  • 不过用处都不是很大,因为只有IE支持它们,且没有什么价值或可以通过其他方式计算得到。

鼠标滚轮事件

  • IE6.0首先实现了mousewheel事件,后来各大浏览器也实现了该事件。当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时(向上或向下)就会触发mousewheel事件。这个事件可以在任何元素上触发,最终会冒泡到document(IE8-)或window(IE9+和其余浏览器)。与mousewheel事件对应的event对象除了之前关于鼠标事件说过的属性外,还有特殊的wheelDelta属性。当用户向前(页面向上),wheelDelta是120的倍数,反之则是-120的倍数(我在Chrome上试过,滚的贼快也就出现过240,大部分时间都是120)。

触摸设备

  • iOS和Android设备没有鼠标,在面向iPhone和iPod中的Safari开发时,需要注意:
    1. 不支持dblclick事件,因为双击浏览器会放大页面。
    2. 轻击可单击元素会触发mousemove 事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生mousedown、mouseup 和click 事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操作的元素(如链接),或
      者那些已经被指定了onclick 事件处理程序的元素。
    3. mousemove 事件也会触发mouseover 和mouseout 事件。
    4. 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel 和scroll 事件。

无障碍性问题

  1. 使用click 事件执行代码。有人指出通过onmousedown 执行代码会让人觉得速度更快,对视力正常的人来说这是没错的。但是,在屏幕阅读器中,由于无法触发mousedown 事件,结果就会造成代码无法执行。
  2. 不要使用onmouseover 向用户显示新的选项。原因同上,屏幕阅读器无法触发这个事件。如果确实非要通过这种方式来显示新选项,可以考虑添加显示相同信息的键盘快捷方式。
  3. 不要使用dblclick 执行重要的操作。键盘无法触发这个事件。

小结

  • 总的来说几个事件还算清楚,不过mouseenter和mouseleavemouseover和mouseout 还是有点混淆。我打算做几个实验来彻底理清他们。
  • 首先mouseover和mouseout是冒泡的,所以意味着我给父元素设置了这两个事件,子类也可以触发他们。看下面的例子:
<!DOCTYPE html><html><head>    <title>example</title>    <style>        td {            width: 500px;            height: 500px;            font-size: 200px;        }    </style></head><body>    <table id="table">        <tr id="tr1">            <td id="td1">td1</td>            <td id="td2">td2</td>        </tr>        <tr id="tr2">            <td id="td3">td3</td>            <td id="td4">td4</td>        </tr>    </table>    <script>        var fun = function(event) {            var targetId = event.target.id?event.target.id:event.target.tagName;            var relatedTargetid = event.relatedTarget.id?event.relatedTarget.id:event.relatedTarget.tagName;            console.log("eventtype:" + event.type + " targetId:" + targetId                    + " relatedTargetid:" + relatedTargetid);        }        var table = document.getElementById("table");        table.onmouseout = fun;    </script></body></html>----------------------鼠标光标从td4->td3->td1->td2->td4->htmleventtype:mouseout targetId:td4 relatedTargetid:td3eventtype:mouseout targetId:td3 relatedTargetid:td1eventtype:mouseout targetId:td1 relatedTargetid:td2eventtype:mouseout targetId:td2 relatedTargetid:td4eventtype:mouseout targetId:td4 relatedTargetid:HTML如果不小心移出浏览器的话relatedTarget会是null,这里我就不修整代码了
  • 要注意mouseoutmouseleave最明显的差异就是该事件会冒泡。所以要是子元素发生了mouseout事件,会冒泡到父元素。然后再执行父元素的事件处理程序。我们把上面代码中的mouseout改成mouseleave,做一样的行为,最终只会弹出一句话:
eventtype:mouseleave targetId:table relatedTargetid:HTML
  • 这是因为mouseleave事件不会冒泡,所以不论子元素发生了何种事件,父元素是不会执行事件处理程序的。而当鼠标指针移出了父元素,也就触发了父元素的mouseleave事件。不过这里有一个疑问。前面关于mouseout的例子中为何没有targetId:table relatedTargetid:HTML,而只有targetId:td4 relatedTargetid:HTML。按照这个理解应该是td4->tr2->table->html
  • (第一次猜想)猜测这里因为td和table中间没有间隙,移出td4的同时也移出了table,所以最终只检测到了td4的mouseout事件(再被冒泡), 所以才有这个现象。
  • 后来我觉得上面这个猜想并不成立,因为从td4->html如果只触发td4的mouseout事件,那是不是也就意味着,这个行为也只触发td4的mouseleave事件。如果是这样,那么根本不会弹出eventtype:mouseleave targetId:table relatedTargetid:HTML。所以我打算为tr2和td4也绑定mouseleave事件处理程序。
    var table = document.getElementById("table");    table.onmouseleave = fun;    var tr2 = document.getElementById("tr2");    tr2.onmouseleave = fun;    var td4 = document.getElementById("td4");    td4.onmouseleave = fun;    //结果如下    //eventtype:mouseleave targetId:td4 relatedTargetid:HTML    //eventtype:mouseleave targetId:tr2 relatedTargetid:HTML    //eventtype:mouseleave targetId:table relatedTargetid:HTML
  • 上面的结果说明从td4->html,是会单独触发td->htmltr->htmltable->html的mouseleave事件的。那么mouseout会如何呢?
    var table = document.getElementById("table");    table.onmouseout = fun;    var tr2 = document.getElementById("tr2");    tr2.onmouseout = fun;    var td4 = document.getElementById("td4");    td4.onmouseout = fun;    //结果是连续打印三条:    //eventtype:mouseout targetId:td4 relatedTargetid:HTML
  • 对于第一条是可以理解的。那么后两条该如何理解呢?我们再看看currentTargetId是什么。
    var fun = function(event) {        var targetId = event.target.id?event.target.id:event.target.tagName;        var currentTargetId = event.currentTarget.id?event.target.id:event.currentTarget.tagName;        var relatedTargetid = event.relatedTarget.id?event.relatedTarget.id:event.relatedTarget.tagName;        console.log("eventtype:" + event.type + " targetId:" + targetId                + " relatedTargetid:" + relatedTargetid + " currentTargetId:" + currentTargetId);    }    var table = document.getElementById("table");    table.onmouseout = fun;    var tr2 = document.getElementById("tr2");    tr2.onmouseout = fun;    var td4 = document.getElementById("td4");    td4.onmouseout = fun;    //事实就是,弹出了三条:    //eventtype:mouseout targetId:td4 relatedTargetid:HTML currentTargetId:td4
  • 这说明了,mouseout事件在子元素直接从父元素中移出,真的只会触发子元素的mouseout事件(然后冒泡给父元素)!!!我想这应该是一种优化吧。毕竟如果只为父元素绑定了mouseout事件,在子元素直接从父元素中移出,不做这种优化将会触发两次以上的mouseout事件这只是我的个人想法)。
  • 对于mouseover也是同理(也有上述我所猜想的优化行为),在子元素中移动,会触发子元素的mouseover事件,然后被父元素冒泡执行。而mouseenter由于没有冒泡,所以子元素触发的mouseenter不会冒泡而执行父元素的事件处理程序。有兴趣的可以改下之前例子的事件名做下测试。