整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML DOM 事件

HTML DOM 事件

HTML DOM 事件允许Javascript在HTML文档元素中注册不同事件处理程序。

事件通常与函数结合使用,函数不会在事件发生前被执行! (如用户点击按钮)。

提示: 在 W3C 2 级 DOM 事件中规范了事件模型。

HTML DOM 事件

DOM: 指明使用的 DOM 属性级别。

鼠标事件

属性描述DOM
onclick当用户点击某个对象时调用的事件句柄。2
oncontextmenu在用户点击鼠标右键打开上下文菜单时触发
ondblclick当用户双击某个对象时调用的事件句柄。2
onmousedown鼠标按钮被按下。2
onmouseenter当鼠标指针移动到元素上时触发。2
onmouseleave当鼠标指针移出元素时触发2
onmousemove鼠标被移动。2
onmouseover鼠标移到某元素之上。2
onmouseout鼠标从某元素移开。2
onmouseup鼠标按键被松开。2

键盘事件

属性描述DOM
onkeydown某个键盘按键被按下。2
onkeypress某个键盘按键被按下并松开。2
onkeyup某个键盘按键被松开。2

框架/对象(Frame/Object)事件

属性描述DOM
onabort图像的加载被中断。 ( <object>)2
onbeforeunload该事件在即将离开页面(刷新或关闭)时触发2
onerror在加载文档或图像时发生错误。 ( <object>, <body>和 <frameset>)
onhashchange该事件在当前 URL 的锚部分发生修改时触发。
onload一张页面或一幅图像完成加载。2
onpageshow该事件在用户访问页面时触发
onpagehide该事件在用户离开当前网页跳转到另外一个页面时触发
onresize窗口或框架被重新调整大小。2
onscroll当文档被滚动时发生的事件。2
onunload用户退出页面。 ( <body> 和 <frameset>)2

表单事件

属性描述DOM
onblur元素失去焦点时触发2
onchange该事件在表单元素的内容改变时触发( <input>, <keygen>, <select>, 和 <textarea>)2
onfocus元素获取焦点时触发2
onfocusin元素即将获取焦点时触发2
onfocusout元素即将失去焦点时触发2
oninput元素获取用户输入时触发3
onreset表单重置时触发2
onsearch用户向搜索域输入文本时触发 ( <input="search">)
onselect用户选取文本时触发 ( <input> 和 <textarea>)2
onsubmit表单提交时触发2

剪贴板事件

属性描述DOM
oncopy该事件在用户拷贝元素内容时触发
oncut该事件在用户剪切元素内容时触发
onpaste该事件在用户粘贴元素内容时触发

打印事件

属性描述DOM
onafterprint该事件在页面已经开始打印,或者打印窗口已经关闭时触发
onbeforeprint该事件在页面即将开始打印时触发

拖动事件

事件描述DOM
ondrag该事件在元素正在拖动时触发
ondragend该事件在用户完成元素的拖动时触发
ondragenter该事件在拖动的元素进入放置目标时触发
ondragleave该事件在拖动元素离开放置目标时触发
ondragover该事件在拖动元素在放置目标上时触发
ondragstart该事件在用户开始拖动元素时触发
ondrop该事件在拖动元素放置在目标区域时触发

多媒体(Media)事件

事件描述DOM
onabort事件在视频/音频(audio/video)终止加载时触发。
oncanplay事件在用户可以开始播放视频/音频(audio/video)时触发。
oncanplaythrough事件在视频/音频(audio/video)可以正常播放且无需停顿和缓冲时触发。
ondurationchange事件在视频/音频(audio/video)的时长发生变化时触发。
onemptied当期播放列表为空时触发
onended事件在视频/音频(audio/video)播放结束时触发。
onerror事件在视频/音频(audio/video)数据加载期间发生错误时触发。
onloadeddata事件在浏览器加载视频/音频(audio/video)当前帧时触发触发。
onloadedmetadata事件在指定视频/音频(audio/video)的元数据加载后触发。
onloadstart事件在浏览器开始寻找指定视频/音频(audio/video)触发。
onpause事件在视频/音频(audio/video)暂停时触发。
onplay事件在视频/音频(audio/video)开始播放时触发。
onplaying事件在视频/音频(audio/video)暂停或者在缓冲后准备重新开始播放时触发。
onprogress事件在浏览器下载指定的视频/音频(audio/video)时触发。
onratechange事件在视频/音频(audio/video)的播放速度发送改变时触发。
onseeked事件在用户重新定位视频/音频(audio/video)的播放位置后触发。
onseeking事件在用户开始重新定位视频/音频(audio/video)时触发。
onstalled事件在浏览器获取媒体数据,但媒体数据不可用时触发。
onsuspend事件在浏览器读取媒体数据中止时触发。
ontimeupdate事件在当前的播放位置发送改变时触发。
onvolumechange事件在音量发生改变时触发。
onwaiting事件在视频由于要播放下一帧而需要缓冲时触发。

动画事件

事件描述DOM
animationend该事件在 CSS 动画结束播放时触发
animationiteration该事件在 CSS 动画重复播放时触发
animationstart该事件在 CSS 动画开始播放时触发

过渡事件

事件描述DOM
transitionend该事件在 CSS 完成过渡后触发。

其他事件

事件描述DOM
onmessage该事件通过或者从对象(WebSocket, Web Worker, Event Source 或者子 frame 或父窗口)接收到消息时触发
onmousewheel已废弃。 使用 onwheel 事件替代
ononline该事件在浏览器开始在线工作时触发。
onoffline该事件在浏览器开始离线工作时触发。
onpopstate该事件在窗口的浏览历史(history 对象)发生改变时触发。
onshow该事件当 <menu> 元素在上下文菜单显示时触发
onstorage该事件在 Web Storage(HTML 5 Web 存储)更新时触发
ontoggle该事件在用户打开或关闭 <details> 元素时触发
onwheel该事件在鼠标滚轮在元素上下滚动时触发

事件对象

常量

静态变量描述DOM
CAPTURING-PHASE当前事件阶段为捕获阶段(3)1
AT-TARGET当前事件是目标阶段,在评估目标事件(1)2
BUBBLING-PHASE当前的事件为冒泡阶段 (2)3

属性

属性描述DOM
bubbles返回布尔值,指示事件是否是起泡事件类型。2
cancelable返回布尔值,指示事件是否可拥可取消的默认动作。2
currentTarget返回其事件监听器触发该事件的元素。2
eventPhase返回事件传播的当前阶段。2
target返回触发此事件的元素(事件的目标节点)。2
timeStamp返回事件生成的日期和时间。2
type返回当前 Event 对象表示的事件的名称。2

方法

方法描述DOM
initEvent()初始化新创建的 Event 对象的属性。2
preventDefault()通知浏览器不要执行与事件关联的默认动作。2
stopPropagation()不再派发事件。2

目标事件对象

方法

方法描述DOM
addEventListener()允许在目标事件中注册监听事件(IE8 = attachEvent())2
dispatchEvent()允许发送事件到监听器上 (IE8 = fireEvent())2
removeEventListener()运行一次注册在事件目标上的监听事件(IE8 = detachEvent())2

事件监听对象

方法

方法描述DOM
handleEvent()把任意对象注册为事件处理程序2

文档事件对象

方法

方法描述DOM
createEvent()2

鼠标/键盘事件对象

属性

属性描述DOM
altKey返回当事件被触发时,"ALT" 是否被按下。2
button返回当事件被触发时,哪个鼠标按钮被点击。2
clientX返回当事件被触发时,鼠标指针的水平坐标。2
clientY返回当事件被触发时,鼠标指针的垂直坐标。2
ctrlKey返回当事件被触发时,"CTRL" 键是否被按下。2
Location返回按键在设备上的位置3
charCode返回onkeypress事件触发键值的字母代码。2
key在按下按键时返回按键的标识符。3
keyCode返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码。2
which返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码。2
metaKey返回当事件被触发时,"meta" 键是否被按下。2
relatedTarget返回与事件的目标节点相关的节点。2
screenX返回当某个事件被触发时,鼠标指针的水平坐标。2
screenY返回当某个事件被触发时,鼠标指针的垂直坐标。2
shiftKey返回当事件被触发时,"SHIFT" 键是否被按下。2

方法

方法描述W3C
initMouseEvent()初始化鼠标事件对象的值2
initKeyboardEvent()初始化键盘事件对象的值3

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!

nmouseenter与onmouseover事件作用:

当鼠标进入(经过)触发事件。

但他们之间有一点小的差别。我们来看这个例子。


我们创建这样的HTML页面结构

然后,给定适当的css样式如下:

这样就达到了平常网站菜单的效果

分别给ul和li添加mouseover事件。

我们给ul与li都加上mouseover事件,那么当鼠标经过li时,会有事件冒泡,会触发ul上面的mouseover事件。一次弹出两个alert窗口。

使用mouseenter事件则不会出现事件冒泡

应用于下拉菜单特效等

有不懂的欢迎私信我哟~

内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《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的变化以推断滚动方向;