MouseEvent鼠标事件和WheelEvent滚轮事件-Javascript

MouseEvent鼠标事件:

DOM2级事件中定义了7个,DOM3级事件增加了2个鼠标事件:

  • click:单击或者回车(一般是左按钮,可以通过键盘和鼠标进行);
  • dbclick:双击(从技术上说,这个事件不是DOM事件规范中规定);
  • mousedown:按下任意鼠标键;
  • mouseup:释放鼠标按钮时触发;
  • mousemove:在元素内部移动时重复地触发;
  • mouseover:当鼠标进入元素时触发;
  • mouseout:在鼠标光标位于一个元素上方,再将其移入另一个元素时触发;又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素;
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发;类似于mouseover,但该事件不冒泡,而且在光标移动到后代元素上不会触发,该事件由IE引入,在DOM3级事件中被纳入规范;
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发,类似于mouseout,但该事件不冒泡,而且在光移动到后代元素上不会触发,该事件由IE引入,在DOM3级事件中被纳入规范;
  • contextmenu:鼠标右击出现上下文菜单时触发,这个事件是在HTML5中定义的,其可以取消;

鼠标事件中还有一类滚轮事件,只包括一个mousewheel事件,但此事件已归WheelEvent类了;

document.addEventListener("click", function(event){
    console.log(event);  // MouseEvent
},false);
document.addEventListener("mousewheel", function(event){
    console.log(event);  // WheelEvent
},false);

可以检测浏览器是否支持所有事件,如:

var isSupported = document.implementation.hasFeature("MouseEvent", "3.0");

页面上的所有元素都支持鼠标事件;除了mouseenter和mouseleave,其他所有鼠标事件都会冒泡,也可以取消,而取消鼠标事件将会影响浏览器的默认行为,也会影响其他事件;

click事件:

在一个元素上被按下和放开时,click事件就会被触发,包括鼠标单击(通常是左按钮)或键盘回车,或者在脚本中为一个对象执行了click()方法也会触发该事件;

var btn = document.getElementById("btn");
btn.click();

在一个focusable元素上单击时,该元素就获得了焦点,就会触发focus事件和click事件;

function handler(event){
console.log(event.type);
}
var txt = document.getElementById("txt");
txt.addEventListener("click", handler,false);
txt.addEventListener("focus", handler,false);

其触发的顺序为:focus、click;

只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;如果mousedown或mouseup中一个被取消,就不会触发click事件,类似只有触发两次click事件才会触发一次dbclick事件;

这4个鼠标事件触发顺序:
mousedown –> mouseup –> click –> mousedown –> mouseup –> click –> dbclick;

mouseover和mouseout事件:
当鼠标进入或移出元素时触发这两个事件;

示例:鼠标悬停改变表格行的背景色,如:

<style>
#mytable{width: 400px; border-collapse: collapse;}
#mytable td{ height: 20px; border: 1px solid #000;}
</style>
<table id="mytable">
<tr>
<td></td> <td></td> <td></td>
</tr>
<!-- 多行 -->
</table>
<script>
// 表格名称、奇数行背景、偶数行背景、鼠标经过背景、点击后背景
function changeBg(table, oddColor, evenColor,overColor, clickColor){
var rows = document.getElementById(table).rows;
for(var i=0; i < rows.length; i++){
var tr = rows[i];
tr.style.backgroundColor = (tr.sectionRowIndex % 2 == 0) ? oddColor : evenColor;
tr.original = true;
tr.addEventListener("click", function(event){
if(this.original){
this.original = false;
this.style.backgroundColor = clickColor;
}else{
this.original = true;
this.style.backgroundColor = (this.sectionRowIndex % 2 == 0) ? oddColor : evenColor;
}
});
tr.addEventListener("mouseover", function(){
if(this.original)
this.style.backgroundColor = overColor;
});
tr.addEventListener("mouseout", function(){
if(this.original)
this.style.backgroundColor = (this.sectionRowIndex % 2 == 0) ? oddColor : evenColor;
});
}
}
changeBg("mytable", "#FFF", "#ccc", "#cfc", "#f00");
</script>

mouseover和mouseout事件会冒泡,当触发mouseout事件时,有可能鼠标真的离开了目标元素,但也有可能是从这个元素移动到它的子元素上,或者从一个子元素移动到另一个子元素,所以在需要判断鼠标的确切位置;

<div id="mydiv">
<div id="div1">div1</div>
<div id="div2">div2</div>
</div>
<script>
var oDiv = document.getElementById("mydiv");
oDiv.addEventListener("mouseover", function(event){
console.log("mouseover:" + event.target.id);
},false);
oDiv.addEventListener("mouseout", function(event){
console.log("mouseout:" + event.target.id);
},false);
</script>

DOM3提供了两个不冒泡的对应版本mouseenter和mouseleave,如:

oDiv.addEventListener("mouseenter", function(event){
console.log("mouseenter:" + event.target.id);
},false);
oDiv.addEventListener("mouseleave", function(event){
console.log("mouseleave:" + event.target.id);
},false);

示例:图片遮罩,如:

<style>
*{margin:0; padding: 0;}
ul,li{list-style: none;}
ul{display:flex; flex-wrap: wrap;}
li{width: 200px;}
li>a{display: block; width: 100%; position: relative;}
li img{width:200px;}
</style>
<ul id="mylist">
<li><a href="#" title="天下第一山"><img src="images/1.jpg"></a></li>
<li><a href="#" title="zeronetwork"><img src="images/2.jpg"></a></li>
<li><a href="#" title="Javascript"><img src="images/3.jpg"></a></li>
<li><a href="#" title="Web前端开发"><img src="images/4.jpg"></a></li>
</ul>
<script>
window.onload = function(){
var mylist = document.getElementById("mylist");
var aList = mylist.getElementsByTagName("a");
for(var i=0,len=aList.length; i<len; i++){
var a = aList[i];
var mask = null;
a.addEventListener("mouseenter", function(event){
mask = this.getElementsByClassName("mask")[0];
if(!mask){
mask = document.createElement("div");
mask.className = "mask";
}
mask.style.backgroundColor = "rgba(0,0,0,0.8)";
var computedStyle = document.defaultView.getComputedStyle(this, null);
mask.style.width = computedStyle.width;
mask.style.height = computedStyle.height;
mask.style.position = "absolute";
mask.style.color = "#FFF";
mask.style.textAlign = "center";
mask.style.lineHeight = computedStyle.height;
mask.innerHTML = this.title;
this.insertBefore(mask, this.firstChild);
},false);
a.addEventListener("mouseleave", function(event){
var mask = this.getElementsByClassName("mask")[0];
console.log(this);
if(mask){
this.removeChild(mask);
}
},false);
}
}
</script>

mouseleave和mouseenter事件的行为与CSS的:hover 伪类非常相似;

mousemove事件,会频繁触发,在其事件处理程序中不能放置计算密集的任务,或者使用事件节流的方式;

鼠标事件对象:

鼠标事件属于MouseEvent类,该类指的是用户与指针设备( 如鼠标 )交互时发生的事件,其继承自UIEvent类;
MouseEvent类定义了一组专属于鼠标事件的属性,描述了当事件发生时鼠标的位置和按键的状态,也包含了是否有辅助键被按下等所有信息;

客户区坐标位置:
clientX与clientY属性:取得鼠标相对于浏览器视口的坐标位置;

var oDiv = document.getElementById("mydiv");
EventUtil.addHandler(oDiv, "click", function(event){
event = EventUtil.getEvent(event);
console.log(event);
console.log(event.clientX + "," + event.clientY);
});

注,这个位置不包括页面滚动的距离,如果加上窗口的滚动偏移量,就会把鼠标位置转换成文档坐标;
所有浏览器也都实现了x和y属性,其是clientX和clientY的别名;

示例:计算鼠标拖动的直线距离,如:

var obj = {};
function downHandler(event){
obj.x = event.x;
obj.y = event.y;
}
function upHandler(event){
obj.mx = event.x - obj.x;
obj.my = event.y - obj.y;
obj.d = Math.sqrt((Math.pow(obj.mx,2) + Math.pow(obj.my,2)));
console.log(obj.mx, obj.my, obj.d);
}
document.addEventListener("mousedown", downHandler, false);
document.addEventListener("mouseup", upHandler, false);

示例:自定义鼠标样式,如:

document.documentElement.style.cursor = "none";
var cursor = document.createElement("span");
cursor.style.width = "20px";
cursor.style.height = "20px";
cursor.style.position = "absolute";
cursor.style.backgroundColor = "#000";
document.body.appendChild(cursor);
document.addEventListener("mousemove", function(event){
cursor.style.left = event.clientX - cursor.offsetWidth / 2 + "px";
cursor.style.top = event.clientY - cursor.offsetHeight / 2 + "px";
},false);

文档坐标位置:
pageX和pageY属性:取得鼠标光标在文档中的位置;

console.log(event.pageX + "," + event.pageY);

这个位置是包括滚动距离的,在文档没有滚动的情况下,pageX、pageY与clientX、clientY值相等;
IE8及以下不支持文档坐标,不过使用客户区坐标和滚动信息可以计算出来,也就是需要用到scrollLeft和scrollTop属性,如:

var oDiv = document.getElementById("mydiv");
EventUtil.addHandler(oDiv, "click", function(event){
event = EventUtil.getEvent(event);
var pageX = event.pageX,
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);
console.log(pageX + ":" + pageY);
});

屏幕坐标位置:screenX与screenY属性:取得相对于屏幕的位置;

console.log(event.screenX + ":" + event.screenY);

元素坐标位置:
offsetX和offsetY属性,返回与目标元素(target)的内填充边(padding edge)在X和Y轴方向上的偏移量;坐标原点为padding区左上角,如果目标元素有边框,鼠标的位置位于边框上,该属性值为负值;

var oDiv = document.getElementById("mydiv");
oDiv.style.position = "relative";
oDiv.style.left = "100px";
oDiv.style.top = "50px";
document.addEventListener("click", function(event){
console.log("offsetX:" + event.offsetX + ",offsetY:" + event.offsetY);
},false);

如果元素滚动了,也包括offsetLeft和offsetTop值;

示例:绘图:

<style>
canvas{border: 1px solid;}
</style>
<canvas id="mydraw" width="560" height="360"></canvas>
<script>
var isDrawing = false;
var x=0, y=0;
var mydraw = document.getElementById("mydraw");
var context = mydraw.getContext("2d");
mydraw.addEventListener("mousedown", function(event){
x = event.offsetX, y = event.offsetY;
isDrawing = true;
});
mydraw.addEventListener("mousemove", function(event){
if(isDrawing === true){
drawLine(context, x, y, event.offsetX, event.offsetY);
x = event.offsetX, y = event.offsetY;
}
});
window.addEventListener("mouseup", function(event){
if(isDrawing === true){
drawLine(context, x, y, event.offsetX, event.offsetY);
x = 0, y = 0;
isDrawing = false;
}
});
function drawLine(content, x1, y1, x2, y2){
context.beginPath();
context.strokeStyle = "black";
context.lineWidth = 1;
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
context.closePath();
}
</script>

movementX和movementY属性:

返回当前事件和上一个mousemove事件之间鼠标在水平或垂直方向上的移动值;

即:currentEvent.movementX = currentEvent.screenX - previousEvent.screenX;

currentEvent.movementY = currentEvent.screenY - previousEvent.screenY;
document.addEventListener("mousemove", function(event){
console.log(event.movementX);
},false);

但IE不支持,并且此属性只有在mousemove事件中才能返回正确的值;

辅助键:

DOM规定了4个属性:shiftkey、ctrlKey、altkey和metaKey,表示当事件发生时shift、ctrl、alt和meta4个键的按下状态,均为布尔值,如果按下为true,反之为false;

var btn = document.getElementById("btn");
EventUtil.addHandler(btn, "click", handler);
function handler(event){
event = EventUtil.getEvent(event);
var keys = new Array();
if(event.shiftKey)
keys.push("shift");
if(event.altKey)
keys.push("alt");
if(event.ctrlKey)
keys.push("ctrl");
if(event.metaKey)
keys.push("meta");
console.log("keys:" + keys.join(","));
}

标准浏览器支持,但IE不支持metaKey属性;

getModifierState(key)方法:返回指定修饰键的当前状态;
参数key可以为Control、Alt、Shift和Meta,注意,大小写是敏感的;

btn.addEventListener("click", function(event){
console.log(event.getModifierState("Control"));
},false);

relatedTarget相关元素:

在发生mouseover和mouseout事件时,会涉及到多个元素;对mouseover而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素(这个相关元素也可以把它叫做次目标元素);对mouseout事件而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素;
DOM通过event对象的relatedTarget属性提供了相关元素的信息,该属性只针于mouseover、mouseout、mouseenter、mouseleave、focusin、focusout、dragenter(拖动元素进入)事件时才有值;对于其他事件,该属性为null;

var oDiv = document.getElementById("mydiv");
oDiv.addEventListener("mouseover", function(event){
console.log(event);
},false);
oDiv.addEventListener("mouseout", function(event){
console.log(event);
},false);

IE8及以下不支持relatedTarget属性,但提供了保存着同样信息不同的属性,在mouseover事件触发时,fromElement属性中保存了相关元素,toElement属性为事件目标;在mouseout事件触发时,toElement属性保存了相关元素,fromElement属性为事件目标;

document.addEventListener("mouseover", function(event){
console.log(event.fromElement);
console.log(event.toElement);
},false);

跨浏览器取得相关元素,添加到eventutil文件中;

getRelatedTarget: function(event){
if(event.relatedTaret)
return event.relatedTaret;
else if(event.toElement)
return event.toElement;
else if(event.fromElement)
return event.fromElement;
else
return null;
}

应用:

var oDiv = document.getElementById("mydiv");
EventUtil.addHandler(oDiv, "mouseout", function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var relatedTarget = EventUtil.getRelatedTarget(event);
console.log(relatedTarget);
console.log(relatedTarget.tagName);
})

鼠标按钮:
对于mousedown和mouseup事件来说,在其event对象存在一个button属性,表示按下或释放的哪个鼠标按钮;可能有3个值:0主按钮;1中间按钮鼠标滚轮);2次按钮;

btn.addEventListener("mouseup", function(event){
console.log(event.button);
},false);

IE8及以下也提供了button,但其值与DOM的button属性有很大差异:0 没有按下按钮;1按下主按钮;2次按钮;3同时按下主次按钮;4中间按钮;5同时按下主和中间按钮;6次和中间按钮;7同时按下三个;

跨浏览器取得button属性,在eventutil文件中添加:

getButton: function(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;
}
}
}

buttons属性:
当鼠标事件触发的时,如果多个鼠标按钮被按下,将会返回一个或者多个代表鼠标按钮的位掩码:

  • 0:没有按键或者是没有初始化;
  • 1:鼠标左键;
  • 2:鼠标右键;
  • 4:鼠标滚轮或者是中键;
  • 8:第四按键 (通常是“浏览器后退”按键);
  • 16:第五按键 (通常是“浏览器前进”);

buttons的值为各键对应值做按位与(+)计算后的值,例如,如果右键(2)和滚轮键(4)被同时按下,buttons的值为 2 + 4 = 6,如:

btn.addEventListener("mousedown", function(event){
console.log(event.button);
console.log(event.buttons);
},false);

属性button和buttons 是不同的,buttons可指示任意鼠标事件中鼠标的按键情况,而 button只能保证在由按下和释放一个或多个按键时触发的事件中获得正确的值;

which属性:
当鼠标事件触发时,表示被按下的按钮,其返回特定按键的数字,0为无、1为左键、2为中间滚轮、3为右键;其是非标准属性,但所有浏览器都支持;

btn.addEventListener("mousedown", function(event){
console.log(event.which);
console.log(event.button);
},false);

注意,此时应该注册mousedown或mouseup事件而不是click事件,因为右击或按下中间滚动不会触发click事件;

detail属性:
DOM2在event对象中提供了detail属性,用于给出有关事件的更多信息;对于鼠标click、mousedown和mouseup事件来说,detail中包含了一个数值,表示目标元素被单击了多少次,其它事件返回0;detail从1开始计数,每次单击都会递增;

console.log(event.detail);

用此属性就可以判断用户是单击、双击还是三击;如果鼠标在mousedown和mouseup之间移动了位置,则detail被重置为0;

IE也通过下列属性为鼠标事件提供了更多信息:

  • altLeft :布尔值,是否按下了Alt,如果为true,则altKey的值也为true;
  • ctrlLeft:布尔值,是否按下了ctrl,如果为true,则ctrlKey的值也为true;
  • shiftLeft:布尔值,是否按下了shift,如果为true,则shiftKey的值也为true;

这些属性只有IE支持;

示例:拖动文档元素,当鼠标按下或释放时,会触发mousedown和mouseup事件,通过这两个事件,可以探测和响应鼠标的拖动;如:

function drag(elementToDrag, event){
    var scroll = {x:0, y:0};
    var startX = event.clientX + scroll.x;
    var startY = event.clientY + scroll.y;

    var origX = elementToDrag.offsetLeft;
    var origY = elementToDrag.offsetTop;

    var deltaX = startX - origX;
    var deltaY = startY - origY;

    if(document.addEventListener){
    document.addEventListener("mousemove", moveHandler, true);
    document.addEventListener("mouseup", upHandler, true);
    }else if(document.attachEvent){

    elementToDrag.setCapture();
    elementToDrag.attachEvent("onmousemove", moveHandler);
    elementToDrag.attachEvent("onmouseup", upHandler);
    // 作为mouseup事件看待鼠标捕获的丢失
    elementToDrag.attachEvent("onlosecapture", upHandler);
    }
    // 处理了这个事件,不让任何其他元素看到它
    if(event.stopPropagation)
    event.stopPropagation();
    else
    event.cancelBubble = true;
    // 现在阻止任何默认操作
    if(event.preventDefault)
    event.preventDefault();
    else
    event.returnValue = false;

    // 当元素正在被拖动时,这就是捕获mousemove事件的处理程序
    // 它用于移动这个元素
    function moveHandler(e){
    if(!e) e = window.event;
    // 移动这个元素到当前鼠标位置
    // 通过滚动条的位置和初始单击的偏移量来调整
    // var scroll = getScrollOffsets();
    var scroll = {x:0,y:0};
    elementToDrag.style.left = (e.clientX + scroll.x - deltaX) + "px";
    elementToDrag.style.top = (e.clientY + scroll.y - deltaY) + "px";
    // 同时不让任何其他元素看到这个事件
    if(e.stopPropagation)
    e.stopPropagation();
    else
    e.cancelBubble = true;
    }
    // 这是捕获在拖动结束时发生的最终mouseup事件的处理程序
    function upHandler(e){
    if(!e) e = window.event;
    // 注销捕获事件处理程序
    if(document.removeEventListener){
    document.removeEventListener("mouseup", upHandler, true);
    document.removeEventListener("mousemove", moveHandler, true);
    }else if(document.detachEvent){
    elementToDrag.detachEvent("onlosecapture", upHandler);
    elementToDrag.detachEvent("onmouseup", upHandler);
    elementToDrag.detachEvent("onmousemove", moveHandler);
    elementToDrag.releaseCapture();
    }
    // 并且不让事件进一步传播
    if(e.stopPropagation)
    e.stopPropagation();
    else
    e.cancelBubble = true;
    }
}

应用:

<div style="position: absolute;left:100px;top:100px; width:150px;background-color: purple;">
<div style="background-color: gray;" onmousedown="drag(this.parentNode, event);">标题栏-拖动我</div>
<p>Lorem ...</p>
</div>

CSS的pointer-events属性:

指定在什么情况下 (如果有) 某个特定的元素可以成为鼠标事件的target;主要用于 SVG元素;
可能的值为:

  • auto:默认效果,对于SVG内容,该值与visiblePainted效果相同;
  • none:元素永远不会成为鼠标事件的target;但是,当其后代元素的pointer-events属性指定其他值时,鼠标事件可以指向后代元素,在鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器;
  • visiblePainted、visibleFill、visibleStroke、visible、painted、fill、stroke、all;

该属性可以:

  • 阻止用户的点击动作产生任何效果;
  • 阻止缺省鼠标指针的显示;
  • 阻止CSS里的hover和active状态的变化触发事件;
  • 阻止JavaScript点击动作触发的事件;
<style>
/链接不会跳转 */
a[href="https://www.zeronetwork.cn/"]{
pointer-events: none;
}
</style>
<a href="https://www.zeronetwork.cn/">零点网络</a>
<script>
var link = document.querySelector("a");
function handler(event){
console.log(event);
}
// 以下均无效
link.addEventListener("click",handler,false);
link.addEventListener("mouseover",handler,false);
link.addEventListener("drag",handler,false);
</script>

此属性可以通过控制台改变,如在控制台输入:document.querySelector("a").style.pointerEvents = "auto";此时,超链接就可以触发了;

<style>
/使所有img对任何鼠标事件(如拖动、悬停、单击等)无反应 */
img{
pointer-events: none;
}
</style>
<img src="images/1.jpg" />
<script>
var img = document.querySelector("img");
function handler(event){
console.log(event);
}
// 以下均无效
img.addEventListener("click",handler,false);
img.addEventListener("mouseover",handler,false);
img.addEventListener("drag",handler,false);
</script>

除了指示该元素不是鼠标事件的目标之外,值none表示鼠标事件“穿透”该元素并且指定该元素“下面”的任何元素;如:

<style>
.container{position: relative; width: 200px; height: 150px;}
.mask{width: 100%; height: 100%; background-color: rgba(0, 0, 0, .5);
position: absolute; pointer-events: none; color:#FFF}
.container img{width: 100%; height: 100%;}
</style>
<div class="container">
<div class="mask"></div>
<a href="https://www.zeronetwork.cn"><img src="images/1.jpg" /></a>
</div>
<script>
var link = document.querySelector(".container>a");
link.addEventListener("mouseover", function(event){
var mask = event.currentTarget.parentNode.querySelector(".mask");
mask.innerHTML = event.currentTarget.title;
},false);
link.addEventListener("mouseout", function(event){
var mask = event.currentTarget.parentNode.querySelector(".mask");
mask.innerHTML = "";
},false);
</script>

示例:取得一个元素的相对鼠标坐标,如:

<style>
.parent{ width:400px; height:400px; padding: 50px; margin:100px; background:#f20; }
.child{ width:200px; height:200px; padding:50px; background:#ff0;}
.child-child{ width:50px; height:50px; background:#00d;}
</style>
<div class="parent" id="parent">
<div class="child">
<div class="child-child"></div>
</div>
</div>
<script>
var parent = document.getElementById("parent");
parent.addEventListener("click",function(event){
console.info(event.offsetX);
});
</script>

使用pointer-events属性后再获取,如为child和child-child类分别添加pointer-events属性,此时打印的值就是相对于parent元素的坐标了;

使用pointer-events来阻止元素成为鼠标事件目标不一定意味着元素上的事件侦听器永远不会触发,如果元素后代明确指定了pointer-events属性并允许其成为鼠标事件的目标,那么指向该元素的任何事件在事件传播过CSS添加pointer-events:none,再为其子元素添加pointer-events:all,此时在子元素上单击就可以触发注册在父元素上的事件处理程序;

位于父元素但不在后代元素上的鼠标活动都不会被父元素和后代元素捕获(鼠标活动将会穿过父元素而指向位于其下面的元素);

var subchild = document.querySelector(".child-child");
subchild.addEventListener("click",function(event){
console.log("child-child");
parent.style.pointerEvents = "auto";
});

该属性也可用来提高滚动时的帧频;例如,当页面滚动时,如果恰巧鼠标悬停在某些元素上,则会触发其上的hover效果或触发onmouseover事件,有可能会造成滚动出现问题,此时,如果对body元素应用pointer-events:none,则会禁用了包括hover在内的鼠标事件,从而提高滚动性能;

<style>
#mydiv:hover{
background-color: lightgreen !important;
}
</style>
<div id="mydiv" style="height: 300px;background-color: purple;"></div>
<div style="height: 1000px;"></div>
<script>
var mydiv = document.getElementById("mydiv");
mydiv.addEventListener("mouseover", function(event){
console.log(event);
},false);
</script>

滚动页面时触发了mouseover事件及hover效果,可以在scroll事件中进行相应的处理,如:

var timeoutId = null;
window.addEventListener("scroll", function(event){
document.body.style.pointerEvents = "none";
if(timeoutId){
clearTimeout(timeoutId);
timeoutId = null;
}else{
timeoutId = setTimeout(function(){
console.log("解禁了");
document.body.style.pointerEvents = "auto";
},500);
}
},false);

部分浏览器不支持该属性,可以判断其支持情况,如:

var supportsPointerEvents = (function(){
var dummy = document.createElement("_");
if(!('pointerEvents' in dummy.style))
return false;
dummy.style.pointerEvents = 'auto';
// 如果是真的属性,则赋值不成功
dummy.style.pointerEvents = 'x';
document.body.appendChild(dummy);
var result = getComputedStyle(dummy).pointerEvents === 'auto';
document.body.removeChild(dummy);
return result;
})();
console.log(supportsPointerEvents);

WheelEvent滚轮事件:

当用户通过鼠标滚轮与页面交互,在垂直方向上滚动页面时(无论向上还是向下),就会触发mousewheel事件;该事件可以在任何元素上触发,最终会冒泡到document或window对象,也可以阻止其默认行为;

document.addEventListener("mousewheel", function(event){
    console.log(event);  // WheelEvent
},false);

WheelEvent类:

表示用户滚动鼠标滚轮或类似输入设备时触发的事件,用以替代MouseWheelEvent和MouseScrollEvent,mousewheel实际上是属于MouseWheelEvent类,而后面要讲的Firefox中的DOMMouseScroll属于MouseScrollEvent类,它们两者都不属于标准,兼容性也不好,为了统一两者,就出现了标准的WheelEvent类;

WheelEvent类继承自MouseEvent类(MouseEvent类继承自UIEvent类),所有也可以把它看作是鼠标事件,对于WheelEvent事件对象来说,其中也保存着大量与MouseEvent事件同样的属性,例如,四对有关获取坐标的属性、which(值为0)、relatedTarget(为null)等等;还包括辅助键的属性;

mousewheel事件中的event对象,除了保存鼠标事件的所有标准信息外,还包含一个特殊的wheelDelta属性,其指定用户滚动滚轮有多远,当用户向前滚动鼠标滚轮时,该属性值是120的倍数,当向后滚动时,该值是-120的倍数;

EventUtil.addHandler(document, "mousewheel", function(event){
    event = EventUtil.getEvent(event);
    console.log(event);
    console.log(event.wheelDelta);
})

如果要判断用户滚动的方向,只要检测wheelDelta属性的正负号即可;在Opera9.5之前的版本中,wheelDelta的值的正负号是颠倒的;

除了wheelDelta属性外,事件对象还有wheelDeltaX和wheelDeltaY属性,并且wheelDelta和wheelDeltaY的值一直相同;

console.log(event.wheelDelta);
console.log(event.wheelDeltaY);
console.log(event.wheelDeltaX);

IE不支持这两个属性;
Firefox不支持mousewheel事件,但支持一个名为DOMMouseScroll的类似事件,也是在鼠标滚轮滚动时触发,它也被视为鼠标事件,也包含与鼠标事件有关的所有鼠标;而有关鼠标滚轮的信息则保存在detail属性中,该属性与wheelDelta作用相同;当向前滚动鼠标滚轮时,该属性返回-3的位数,反之返回3的位数;

可以把该事件添加到页面中的任何元素,而且该事件会冒泡到window对象;

EventUtil.addHandler(document, "DOMMouseScroll", function(event){
    event = EventUtil.getEvent(event);
    console.log(event);
    console.log(event.detail);  // 向前为-3,向后是3
})

detail属性值与wheelDelta的值的关系是:wheelDelta等于detail乘以-40;

可以跨浏览器取得鼠标滚轮增值(delta),并添加到eventutil.js中,如:

    getWheelDelta: function(event){
        if(event.wheelDelta){
            return event.wheelDelta;
        }else
            return -event.detail 40;
    }

应用时,需要同时注册mousewheel和DOMMouseScroll事件,如:

function handlerMouseWheel(event){
    event = EventUtil.getEvent(event);
    var delta = EventUtil.getWheelDelta(event);
    console.log(delta);
}
EventUtil.addHandler(document, "mousewheel", handlerMouseWheel);
EventUtil.addHandler(document, "DOMMouseScroll", handlerMouseWheel);

DOMMouseEvent事件对象中还有一个axis属性,其返回一个long型常量值,表明鼠标滚轮滚动的方向,当返回常量HORIZONTAL_AXIS,值为1时,表示由鼠标滚轮的横向滚动触发的,当返回VERTICAL_AXIS,值为2时,表示是由鼠标滚轮的纵向滚动触发的;

wheel事件:
DOM3事件定义了一个名为wheel事件,用于替代mousewheel和DOMMouseScroll事件;事件对象中保存着deltaX、deltaY及deltaZ属性:用来获取三个不同的鼠标滚轴;大多数鼠标滚轮是一维或二维的,所以并不能使用deltaZ属性;

EventUtil.addHandler(document, "wheel", function(event){
    event = EventUtil.getEvent(event);
    console.log(event);  // WheelEvent
    console.log(event.wheelDelta);  // -120
    console.log(event.wheelDeltaY);  // -120
    console.log(event.deltaX);  // -0
    console.log(event.deltaY);  // chrome返回100,Firefox返回63
    console.log(event.deltaZ);  // 0
});

这些值必须乘以-1.2,才和mousewheel事件的wheelDelta值和正负号相匹配;

wheel事件对象还有一个deltaMode属性,只读,其返回long常量值,表示各delta*的值的单位,其值及所表示的单位如下:

常量值描述

  • DOM_DELTA_PIXEL0x00滚动量单位为像素
  • DOM_DELTA_LINE0x01滚动量单位为行
  • DOM_DELTA_PAGE0x02滚动量单位为页
console.log(event.deltaMode);

示例:在Enclose.js文件中定义enclose()函数,可以把一个图片装载到一个容器中,并且能移动这个容器,也能改变容器的大小,如:

function enclose(content, framewidth, frameheight, contentX, contentY){
    // 这些参数不仅仅是初始值,它们保存当前状态,能被mousewheel处理程序使用和修改
    framewidth = Math.max(framewidth, 50);
    frameheight = Math.max(frameheight, 50);
    contentX = Math.min(contentX, 0) || 0;
    contentY = Math.min(contentY, 0) || 0;
    // 创建frame元素,且设置CSS类和样式
    var frame = document.createElement("div");
    frame.className = "enclose";
    frame.style.width = framewidth + "px";
    frame.style.height = frameheight + "px";
    frame.style.overflow = "hidden"; // 没有滚动条,不能溢出
    frame.style.boxSizing = "border-box"; // 能简化调整frame大小的计算
    content.parentNode.insertBefore(frame, content);
    frame.appendChild(content);
    // 确定元素相对于frame的位置
    content.style.position = "relative";
    content.style.left = contentX + "px";
    content.style.top = contentY + "px";
    var isFirefox = (navigator.userAgent.indexOf('Gecko') != -1);
    // 注册mousewheel事件处理程序
    frame.onwheel = wheelHandler;
    frame.onmousewheel = wheelHandler;
    if(isFirefox)
        frame.addEventListener("DOMMouseScroll", wheelHandler, false);
    function wheelHandler(event){
        var e = event || window.event;
        var deltaX = e.deltaX / 3.33 || // wheel事件
                   e.wheelDeltaX / 4 || // mousewheel事件
                                   0    // 属性未定义
        var deltaY = e.deltaY / 3.33 || 
                   e.wheelDeltaY / 4 ||
    (e.wheelDeltaY === undefined &&     // 如果没有2D属性
                   e.wheelDelta / 4) || // 就使用1D的滚轮属性
                     e.detail -1.2 || // DOMMouseScroll事件
                                   0;   // 属性未定义
        if(isFirefox && e.type !== "DOMMouseScroll")
            frame.removeEventListener("DOMMouseScroll", wheelHandler, false);
        // 获取内容元素的当前尺寸
        var contentbox = content.getBoundingClientRect();
        var contentwidth = contentbox.right - contentbox.left;
        var contentheight = contentbox.bottom - contentbox.top;
        // 如果按下Alt键,就可以调整frame大小
        if(e.altKey){
            if(deltaX){
                framewidth -= deltaX; // 新宽度,但不能比内容大
                framewidth = Math.min(framewidth, contentwidth);
                framewidth = Math.max(framewidth, 50); // 不能小于50
                frame.style.width = framewidth + "px";
            }
            if(deltaY){
                frameheight -= deltaY; 
                frameheight = Math.min(frameheight, contentheight);
                frameheight = Math.max(frameheight - deltaY, 50);
                frame.style.height = frameheight + "px";
            }
        }else{ // 没有按Alt键,就可以平移frame中的内容
            if(deltaX){
                var minoffset = Math.min(framewidth - contentwidth, 0);
                // 把deltaX添加到contentX中,但不能小于minoffset
                contentX = Math.max(contentX + deltaX, minoffset);
                contentX = Math.min(contentX, 0);
                content.style.left = contentX + "px";
            }
            if(deltaY){
                var minoffset = Math.min(frameheight - contentheight, 0);
                contentY = Math.max(contentY + deltaY, minoffset);
                contentY = Math.min(contentY, 0);
                content.style.top = contentY + "px";
            }
        }
        if(e.preventDefault)
            e.preventDefault();
        if(e.stopPropagation)
            e.stopPropagation();
        e.cancelBubble = true;
        e.returnValue = false;
        return false;
    }
}

应用:

<style>
div.enclose{border: 10px solid; margin:10px}
</style>
<img id="content" src="images/1.jpg" />
<script>
window.onload = function(){
    enclose(document.getElementById("content"),400,200,-200,-300);
}
</script>

不要混淆wheel事件和scroll事件:wheel事件的默认动作取决于浏览器实现,因此wheel事件不一定会触发scroll事件;即便滚轮事件引发了文档内容的滚动行为,也不表示wheel事件中的delta*值恰好反映文档内容的滚动方向;不要依赖delta*属性获知文档内容的滚动方向,可在文档内容滚动事件中监视target的scrollLeft和scrollTop的变化以推断滚动方向;

为您推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注