您的位置:首页 > Web前端 > JavaScript

JavaScript 事件

2016-04-26 17:13 627 查看

第1章 事件入门

JavaScript 事件是由访问 Web 页面的用户引起的一系列操作,例如:用户点击。当用户执行某些操作的时候,再去执行一系列代码。

事件介绍

事件一般是用于浏览器和用户操作进行交互。最早是 IE 和 Netscape Navigator 中出现 ,作为分担服务器端运算负载的一种手段。 直到几乎所有的浏览器都支持事件处理。 而 DOM2级规范开始尝试以一种复合逻辑的方式标准化 DOM 事件。IE9、Firefox、Opera、Safari 和Chrome 全都已经实现了“DOM2 级事件”模块的核心部分。IE8 之前浏览器仍然使用其专有事件模型。

事件模型

JavaScript 有三种事件模型:内联模型脚本模型DOM2 模型

内联模型

这种模型是最传统接单的一种处理事件的方法。 在内联模型中, 事件处理函数是 HTML标签的一个属性,用于处理指定事件。虽然内联在早期使用较多,但它是和 HTML 混写的 ,并没有与 HTML 分离。

//在 HTML 中把事件处理函数作为属性执行 JS 代码
<input type="button" value="按钮" onclick="alert('Lee');" /> //注意单双引号
//在 HTML 中把事件处理函数作为属性执行 JS 函数
<input type="button" value="按钮" onclick="box();" /> //执行 JS 的函数


PS:函数不得放到 window.onload 里面,这样就看不见了。

脚本模型

由于内联模型违反了 HTML 与 JavaScript 代码层次分离的原则。为了解决这个问题, 我们可以在 JavaScript 中处理事件。这种处理方式就是脚本模型。

var input = document.getElementsByTagName('input')[0]; //得到 input 对象
input.onclick = function () { //匿名函数执行
alert('Lee');
};


PS:通过匿名函数,可以直接触发对应的代码。也可以通过指定的函数名赋值的方式来执行函数(赋值的函数名不要跟着括号)。input.onclick = box; //把函数名赋值给事件处理函数

第2章 事件处理函数(事件句柄,Event Handlers)

JavaScript 可以处理的事件类型为:鼠标事件键盘事件HTML 事件

JavaScript 事件处理函数及其使用列表

事件处理函数影响的元素何时发生
onabort图像当图像加载被中断时
onblur窗口、框架、所有表单对象当焦点从对象上移开时
onchange输入框,选择框和文本区域当改变一个元素的值且失去焦点时
onclick链接、按钮、表单对象、图像映射区域当用户单击对象时
ondblclick链接、按钮、表单对象当用户双击对象时
ondragdrop窗口当用户将一个对象拖放到浏览器窗口时
onError脚本当脚本中发生语法错误时
onfocus窗口、框架、所有表单对象当单击鼠标或者将鼠标移动聚焦到窗口或框架时
onkeydown文档、图像、链接、表单当按键被按下时
onkeypress文档、图像、链接、表单当按键被按下然后松开时
onkeyup文档、图像、链接、表单当按键被松开时
onload主题、框架集、图像文档或图像加载后
onunload主体、框架集文档或框架集卸载后
onmouseout链接当图标移除链接时
onmouseover链接当鼠标移到链接时
onmove窗口当浏览器窗口移动时
onreset表单复位按钮单击表单的 reset 按钮
onresize窗口当选择一个表单对象时
onselect表单元素当选择一个表单对象时
onsubmit表单当发送表格到服务器时
PS:所有的事件处理函数都会都有两个部分组成,on + 事件名称,例如 click 事件的事件处理函数就是:onclick。在这里,我们主要谈论脚本模型的方式来构建事件,违反分离原则的内联模式,我们忽略掉。对于每一个事件,它都有自己的触发范围和方式,如果超出了触发范围和方式,事件处理将失效。

第3章 事件对象

JavaScript 事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能。 最方便和强大的就是事件对象, 他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的捕获/冒泡流的函数。

3.1 事件对象

事件处理函数的一个标准特性是, 以某些方式访问的事件对象包含有关于当前事件的上下文信息。

事件处理三部分组成:对象.事件处理函数=函数。例如:单击文档任意处。

document.onclick = function () {
alert('Lee');
};


PS:以上程序的名词解释:click 表示一个事件类型,单击。onclick 表示一个事件处理函数或绑定对象的属性(或者叫事件监听器、侦听器)。document 表示一个绑定的对象,用于触发某个元素区域。function()匿名函数是被执行的函数,用于触发后执行。

除了用匿名函数的方法作为被执行的函数,也可以设置成独立的函数。

document.onclick = box; //直接赋值函数名即可,无须括号
function box() {
`alert('Lee');
}


this 关键字和上下文

在面向对象那章我们了解到:在一个对象里,由于作用域的关系,this 代表着离它最近对象。

var input = document.getElementsByTagName('input')[0];
input.onclick = function () {
alert(this.value); //HTMLInputElement,this 表示 input 对象
};


从上面的拆分,我们并没有发现本章的重点:事件对象。那么事件对象是什么?它在哪里呢?当触发某个事件时,会产生一个事件对象,这个对象包含着所有与事件有关的信息 。包括导致事件的元素、事件的类型、以及其它与特定事件相关的信息。

事件对象,我们一般称作为 event 对象,这个对象是浏览器通过函数把这个对象作为参数传递过来的。那么首先,我们就必须验证一下,在执行函数中没有传递参数,是否可以得到隐藏的参数。

function box() { //普通空参函数
alert(arguments.length); //0,没有得到任何传递的参数
}
input.onclick = function () { //事件绑定的执行函数
alert(arguments.length); //1,得到一个隐藏参数
};


通过上面两组函数中, 我们发现, 通过事件绑定的执行函数是可以得到一个隐藏参数的 。说明浏览器会自动分配一个参数,这个参数其实就是 event 对象。

input.onclick = function () {
alert(arguments[0]); //MouseEvent,鼠标事件对象
};


上面这种做法比较累,那么比较简单的做法是,直接通过接收参数来得到即可。

input.onclick = function (evt) { //接受 event 对象,名称不一定非要 event
alert(evt); //MouseEvent,鼠标事件对象
};


直接接收 event 对象,是 W3C 的做法,IE 不支持,IE 自己定义了一个 event 对象,直接在 window.event 获取即可。

input.onclick = function (evt) {
var e = evt || window.event; //实现跨浏览器兼容获取 event 对象
alert(e);
};


3.2鼠标事件对象

鼠标事件是 Web 上面最常用的一类事件,毕竟鼠标还是最主要的定位设备。那么通过事件对象可以获取到鼠标按钮信息和屏幕坐标获取等。

3.2.1 鼠标按钮

只有在主鼠标按钮被单击时(常规一般是鼠标左键)才会触发 click 事件,因此检测按钮的信息并不是必要的。但对于 mousedown 和 mouseup 事件来说,则在其 event 对象存在一个 button 属性,表示按下或释放按钮。

非 IE(W3C)中的 button 属性

说明
0表示主鼠标按钮(常规一般是鼠标左键)
1表示中间的鼠标按钮(鼠标滚轮按钮)
2表示次鼠标按钮(常规一般是鼠标右键)
IE 中的 button 属性

说明
0表示没有按下按钮
1表示主鼠标按钮(常规一般是鼠标左键)
2表示次鼠标按钮(常规一般是鼠标右键)
3表示同时按下了主、次鼠标按钮
4表示按下了中间的鼠标按钮
5表示同时按下了主鼠标按钮和中间的鼠标按钮
6表示同时按下了次鼠标按钮和中间的鼠标按钮
7表示同时按下了三个鼠标按钮
PS:在绝大部分情况下,我们最多只使用主次中三个单击键,IE 给出的其他组合键一般无法使用上。所以,我们只需要做上这三种兼容即可。

function getButton(evt) { //跨浏览器左中右键单击相应
var e = evt || window.event;
if (evt) { //Chrome 浏览器支持 W3C 和 IE
return e.button; //要注意判断顺序
} else if (window.event) {
switch(e.button) {
case 1 :
return 0;
case 4 :
return 1;
case 2 :
return 2;
}
}
}
document.onmouseup = function (evt) { //调用
if (getButton(evt) == 0) {
alert('按下了左键!');
} else if (getButton(evt) == 1) {
alert('按下了中键!');
} else if (getButton(evt) == 2) {
alert('按下了右键!' );
}
};


3.2.2 可视区及屏幕坐标

事件对象提供了两组来获取浏览器坐标的属性, 一组是页面可视区左边, 另一组是屏幕坐标。

坐标属性

属性说明
clientX可视区 X 坐标,距离左边框的位置
clientY可视区 Y 坐标,距离上边框的位置
screenX屏幕区 X 坐标,距离左屏幕的位置
screenY屏幕区 Y 坐标,距离上屏幕的位置
document.onclick = function (evt) {
var e = evt || window.event;
alert(e.clientX + ',' + e.clientY);
alert(e.screenX + ',' + e.screenY);
};


3.2.3 修改键

有时,我们需要通过键盘上的某些键来配合鼠标来触发一些特殊的事件。这些键为:

Shfit、Ctrl、Alt 和 Meat(Windows 中就是 Windows 键,苹果机中是 Cmd 键),它们经常被用来修改鼠标事件和行为,所以叫修改键。

修改键属性

属性说明
shiftKey判断是否按下了 Shfit 键
ctrlKey判断是否按下了 ctrlKey 键
altKey判断是否按下了 alt 键
metaKey判断是否按下了 windows 键,IE 不支持
function getKey(evt) {
var e = evt || window.event;
var keys = [];
if (e.shiftKey) keys.push('shift'); //给数组添加元素
if (e.ctrlKey) keys.push('ctrl');
if (e.altKey) keys.push('alt');
return keys;
}
document.onclick = function (evt) {
alert(getKey(evt));
};


4.3键盘事件对象

用户在使用键盘时会触发键盘事件。 “DOM2 级事件”最初规定了键盘事件,结果又删除了相应的内容。最终还是使用最初的键盘事件,不过 IE9 已经率先支持“DOM3”级键盘事件。

4.3.1 键码

在发生 keydown 和 keyup 事件时,event 对象的 keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符集,keyCode 属性的值与 ASCII 码中对应小写字母或数字的编码相同。字母中大小写不影响。

document.onkeydown = function (evt) {
alert(evt.keyCode); //按任意键,得到相应的 keyCode
};


不同的浏览器在 keydown 和 keyup 事件中,会有一些特殊的情况:

在 Firefox 和 Opera 中,分号键时 keyCode 值为 59,也就是 ASCII 中分号的编码;而 IE和 Safari 返回 186,即键盘中按键的键码。

PS:其他一些特殊情况由于浏览器版本太老和市场份额太低,这里不做补充。

4.3.2字符编码

Firefox、Chrome 和 Safari 的 event 对象都支持一个 charCode 属性,这个属性只有在发生 keypress 事件时才包含值,而且这个值是按下的那个键所代表字符的 ASCII 编码。此时的 keyCode 通常等于 0 或者也可能等于所按键的编码。IE 和 Opera 则是在 keyCode 中保存字符的 ASCII 编码。

function getCharCode(evt) {
var e = evt || window.event;
if (typeof e.charCode == 'number') {
return e.charCode;
} else {
return e.keyCode;
}
}


PS:可以使用 String.fromCharCode()将 ASCII 编码转换成实际的字符。

keyCode 和 charCode 区别如下:比如当按下“a 键(重视是小写的字母)时,

在 Firefox 中会获得

keydown: keyCode is 65 charCode is 0

keyup: keyCode is 65 charCode is 0

keypress: keyCode is 0 charCode is 97

在 IE 中会获得

keydown: keyCode is 65 charCode is undefined

keyup: keyCode is 65 charCode is undefined

keypress: keyCode is 97 charCode is undefined

而当按下 shift 键时,在 Firefox 中会获得

keydown:keyCode is 16 charCode is 0

keyup: keyCode is 16 charCode is 0

在 IE 中会获得

keydown:keyCode is 16 charCode is undefined

keyup: keyCode is 16 charCode is undefined

keypress:不会获得任何的 charCode 值,因为按 shift 并没输入任何的字符,并且也不会触发 keypress 事务

PS:在 keydown 事务里面,事务包含了 keyCode – 用户按下的按键的物理编码。

在 keypress 里,keyCode 包含了字符编码,即默示字符的 ASCII 码。如许的情势实用于所有的浏览器 – 除了火狐,它在 keypress 事务中的 keyCode 返回值为 0。

4.4 W3C与 IE

在标准的 DOM 事件中,event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样

W3C 中 event 对象的属性和方法

属性/方法类型读/写说明
bubblesBoolean只读表明事件是否冒泡
cancelableBoolean只读表明是否可以取消事件的默认行为
currentTargetElement只读其事件处理程序当前正在处理事件的那个元素
detailInteger只读与事件相关的细节信息
eventPhaseInteger只读调用事件处理程序的阶段:1 表示捕获阶段,2 表示“处理目标” ,3 表示冒泡阶段
preventDefault()Function只读取消事件的默认行为。 如cancelabel是 true,则可以使用这个方法
stopPropagation()Function只读取消事件的进一步捕获或冒泡。如果bubbles 为 true, 则可以使用这个方法
targetElement只读事件的目标
typeString只读被触发的事件的类型
viewAbstractView只读与事件关联的抽象视图。等同于发生事件的 window 对象
IE 中 event 对象的属性

属性类型读/写说明
cancelBubbleBoolean读/写默认值为 false,但将其设置为 true 就可以取消事件冒泡
returnValueBoolean读/写默认值为 true, 但将其设置为 false 就可以取消事件的默认行为
srcElementElement只读事件的目标
typeString只读被触发的事件类型
W3C 中的 target 和 IE 中的 srcElement

在这里,我们只看所有浏览器都兼容的属性或方法。首先第一个我们了解一下 W3C 中的 target 和 IE 中的 srcElement,都表示事件的目标。

function getTarget(evt) {
var e = evt || window.event;
return e.target || e.srcElement; //兼容得到事件目标 DOM 对象
}
document.onclick = function (evt) {
var target = getTarget(evt);
alert(target);
};


事件流

事件流是描述的从页面接受事件的顺序,当几个都具有事件的元素层叠在一起的时候 ,那么你点击其中一个元素, 并不是只有当前被点击的元素会触发事件, 而层叠在你点击范围的所有元素都会触发事件。事件流包括两种模式:冒泡捕获

事件冒泡

是从里往外逐个触发。事件捕获,是从外往里逐个触发。那么现代的浏览器默认情况下都是冒泡模型,而捕获模式则是早期的 Netscape 默认情况。而现在的浏览器要使用 DOM2 级模型的事件绑定机制才能手动定义事件流模式。



document.onclick = function () {
alert('我是 document');
};
document.documentElement.onclick = function () {
alert('我是 html');
};
document.body.onclick = function () {
alert('我是 body');
};
document.getElementById('box').onclick = function () {
alert('我是 div');
};
document.getElementsByTagName('input')[0].onclick = function () {
alert('我是 input');
};


PS::在阻止冒泡的过程中,W3C 和 IE 采用的不同的方法,那么我们必须做一下兼容。

function stopPro(evt) {
var e = evt || window.event;
window.event ? e.cancelBubble = true : e.stopPropagation();
}


第4章 事件绑定及深入

事件绑定分为两种:

传统事件绑定(内联模型,脚本模型),

现代事件绑定

(DOM2 级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。

4.1 传统事件绑定的问题

传统事件绑定有内联模型和脚本模型,内联模型我们不做讨论,基本很少去用。先来看一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。

var box = document.getElementById('box'); //获取元素
box.onclick = function () { //元素点击触发事件
alert('Lee');
};


问题一:一个事件处理函数触发两次事件

window.onload = function () { //第一组程序项目或第一个 JS 文件

alert(‘Lee’);

};

window.onload = function () { //第二组程序项目或第二个 JS 文件

alert(‘Mr.Lee’);

};

当两组程序或两个 JS 文件同时执行的时候,后面一个会把前面一个完全覆盖掉。导致前面的 window.onload 完全失效了。

解决覆盖问题,我们可以这样去解决:

window.onload = function () { //第一个要执行的事件,会被覆盖
alert('Lee');
};
if (typeof window.onload == 'function') { //判断之前是否有 window.onload
var saved = null; //创建一个保存器
saved = window.onload; //把之前的 window.onload 保存起来
}
window.onload = function () { //最终一个要执行事件
if (saved) saved(); //执行之前一个事件
alert('Mr.Lee'); //执行本事件的代码
};


问题二:事件切换器

box.onclick = toBlue; //第一次执行 toBlue()
function toRed() {
this.className = 'red';
this.onclick = toBlue; //第三次执行 toBlue(),然后来回切换
}
function toBlue() {
this.className = 'blue';
this.onclick = toRed; //第二次执行 toRed()
}


这个切换器在扩展的时候,会出现一些问题:

如果增加一个执行函数,那么会被覆盖

box.onclick = toAlert; //被增加的函数

box.onclick = toBlue; //toAlert 被覆盖了

如果解决覆盖问题,就必须包含同时执行,但又出新问题

box.onclick = function () { //包含进去,但可读性降低

toAlert(); //第一次不会被覆盖,但第二次又被覆盖

toBlue.call(this); //还必须把 this 传递到切换器里

};

综上的三个问题:覆盖问题可读性问题this 传递问题。我们来创建一个自定义的事件处理函数,来解决以上三个问题。

function addEvent(obj, type, fn) { //取代传统事件处理函数
var saved = null; //保存每次触发的事件处理函数
if (typeof obj['on' + type] == 'function') { //判断事件是否存在
saved = obj['on' + type]; //如果有,保存上一个事件
}
obj['on' + type] = function () { //然后执行
if (saved) saved(); //执行上一个
fn.call(this); //执行函数,把 this 传递过去
};
}
addEvent(window, 'load', function () { //执行到了
alert('Lee');
});
addEvent(window, 'load', function () { //执行到了
alert('Mr.Lee');
});


PS:以上编写的自定义事件处理函数,还有一个问题没有处理,就是两个相同函数名的函数误注册了两次或多次,那么应该把多余的屏蔽掉。那我们就需要把事件处理函数进行遍历,如果有同样名称的函数名就不添加即可。(这里就不做了)

addEvent(window, 'load', init); //注册第一次
addEvent(window, 'load', init); //注册第二次,应该忽略
function init() {
alert('Lee');
}


用自定义事件函数注册到切换器上查看效果:

addEvent(window, 'load', function () {
var box = document.getElementById('box');
addEvent(box, 'click', toBlue);
});
function toRed() {
this.className = 'red';
addEvent(this, 'click', toBlue);
}
function toBlue() {
this.className = 'blue';
addEvent(this, 'click', toRed);
}


PS:当你单击很多很多次切换后,浏览器直接卡死,或者弹出一个错误:too much recursion(太多的递归)。主要的原因是,每次切换事件的时候,都保存下来,没有把无用的移除,导致越积越多,最后卡死。

function removeEvent(obj, type) {
if (obj['on'] + type) obj['on' + type] = null; //删除事件处理函数
}


以上的删除事件处理函数只不过是一刀切的删除了, 这样虽然解决了卡死和太多递归的问题。但其他的事件处理函数也一并被删除了,导致最后得不到自己想要的结果。如果想要只删除指定的函数中的事件处理函数,那就需要遍历,查找。(这里就不做了)

4.2 W3C事件处理函数

“DOM2 级事件”定义了两个方法,用于添加事件和删除事件处理程序的操作:addEventListener()和 removeEventListener()。

所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:事件名函数冒泡或捕获的布尔值(true 表示捕获,false 表示冒泡)。

window.addEventListener('load', function () {
alert('Lee');
}, false);
window.addEventListener('load', function () {
alert('Mr.Lee');
}, false);


PS:W3C 的现代事件绑定比我们自定义的好处就是:

可以解决覆盖事件绑定;

可以屏蔽相同的函数;

可以设置冒泡和捕获。

window.addEventListener('load', init, false); //第一次执行了
window.addEventListener('load', init, false); //第二次被屏蔽了
function init() {
alert('Lee');
}


事件切换器

window.addEventListener('load', function () {
var box = document.getElementById('box');
box.addEventListener('click', function () { //不会被误删
alert('Lee');
}, false);
box.addEventListener('click', toBlue, false); //引入切换也不会太多递归卡死
}, false);
function toRed() {
this.className = 'red';
this.removeEventListener('click', toRed, false);
this.addEventListener('click', toBlue, false);
}
function toBlue() {
this.className = 'blue';
this.removeEventListener('click', toBlue, false);
this.addEventListener('click', toRed, false);
}


设置冒泡和捕获阶段

之前我们上一章了解了事件冒泡,即从里到外触发。我们也可以通过 event 对象来阻止某一阶段的冒泡。那么 W3C 现代事件绑定可以设置冒泡和捕获。

document.addEventListener('click', function () {
alert('document');
}, true); //把布尔值设置成 true,则为捕获
box.addEventListener('click', function () {
alert('Lee');
}, true); //把布尔值设置成 false,则为冒泡


综上所述,W3C是比较完美的解决了事件绑定中的一些问题,但是IE8即以前的版本并不支持,而是采用了自己的事件绑定处理,当然IE9已经完全支持了

4.3 IE事件处理函数

IE 实现了与 DOM 中类似的两个方法:attachEvent()和 detachEvent()。、这两个方法接受相同的参数:事件名称函数

在使用这两组函数的时候,先把区别说一下:

IE 不支持捕获,只支持冒泡;

IE 添加事件不能屏蔽重复的函数;

IE 中的 this 指向的是 window 而不是 DOM 对象。

在传统事件上,IE 是无法接受到 event 对象的,但使用了 attchEvent()却可以,但有些区别。

window.attachEvent('onload', function () {
var box = document.getElementById('box');
box.attachEvent('onclick', toBlue);
});
function toRed() {
var that = window.event.srcElement;
that.className = 'red';
that.detachEvent('onclick', toRed);
that.attachEvent('onclick', toBlue);
}
function toBlue() {
var that = window.event.srcElement;
that.className = 'blue';
that.detachEvent('onclick', toBlue);
that.attachEvent('onclick', toRed);
}


PS:IE 不支持捕获,无解。IE 不能屏蔽,需要单独扩展或者自定义事件处理。IE 不能传递 this,可以 call 过去。

window.attachEvent('onload', function () {
var box = document.getElementById('box');
box.attachEvent('onclick', function () {
alert(this === window); //this 指向的 window
});
});
window.attachEvent('onload', function () {
var box = document.getElementById('box');
box.attachEvent('onclick', function () {
toBlue.call(box); //把 this 直接 call 过去
});
});
function toThis() {
alert(this.tagName);
}


在传统绑定上,IE 是无法像 W3C 那样通过传参接受 event 对象,但如果使用了attachEvent()却可以。

box.onclick = function (evt) {
alert(evt); //undefined
}
box.attachEvent('onclick', function (evt) {
alert(evt); //object
alert(evt.type); //click
});
box.attachEvent('onclick', function (evt) {
alert(evt.srcElement === box); //true
alert(window.event.srcElement === box); //true
});


最后,为了让 IE 和 W3C 可以兼容这个事件切换器,我们可以写成如下方式:

function addEvent(obj, type, fn) { //添加事件兼容
if (obj.addEventListener) {
obj.addEventListener(type, fn);
} else if (obj.attachEvent) {
obj.attachEvent('on' + type, fn);
}
}
function removeEvent(obj, type, fn) { //移除事件兼容
if (obj.removeEventListener) {
obj.removeEventListener(type, fn);
} else if (obj.detachEvent) {
obj.detachEvent('on' + type, fn);
}
}
function getTarget(evt) { //得到事件目标
if (evt.target) {
return evt.target;
} else if (window.event.srcElement) {
return window.event.srcElement;
}
}


PS:调用忽略,IE 兼容的事件,如果要传递 this,改成 call 即可。

PS:IE 中的事件绑定函数 attachEvent()和 detachEvent()可能在实践中不去使用,有几个原因:

IE9 就将全面支持 W3C 中的事件绑定函数;

IE的事件绑定无法传递this;

IE的事件绑定函数不支持捕获;

同一个函数注册绑定后,没有屏蔽掉;

有内存泄漏的问题 。

至于怎么替代,我们将在以后的项目课程中探讨。

4.4 事件对象的其他补充

4.4.1relatedTarget,,fromElement,toElement

在 W3C 提供了一个属性:relatedTarget;这个属性可以在 mouseover 和 mouseout 事件中获取从哪里移入和从哪里移出的 DOM 对象。

box.onmouseover = function (evt) { //鼠标移入 box
alert(evt.relatedTarget); //获取移入 box 最近的那个元素对象
} //span
box.onmouseout = function (evt) { //鼠标移出 box
alert(evt.relatedTarget); //获取移出 box 最近的那个元素对象
} //span


IE 提供了两组分别用于移入移出的属性: fromElementtoElement, 分别对应 mouseover和 mouseout。

box.onmouseover = function (evt) { //鼠标移入 box
alert(window.event.fromElement.tagName); //获取移入 box 最近的那个元素对象 span
}
box.onmouseout = function (evt) { //鼠标移入 box
alert(window.event.toElement.tagName); //获取移入 box 最近的那个元素对象 span
}


PS:fromElement 和 toElement 如果分别对应相反的鼠标事件,没有任何意义。

剩下要做的就是跨浏览器兼容操作:

function getTarget(evt) {
var e = evt || window.event; //得到事件对象
if (e.srcElement) { //如果支持 srcElement,表示 IE
if (e.type == 'mouseover') { //如果是 over
return e.fromElement; //就使用 from
} else if (e.type == 'mouseout') { //如果是 out
return e.toElement; //就使用 to
}
} else if (e.relatedTarget) { //如果支持 relatedTarget,表示 W3C
return e.relatedTarget;
}
}


4.4.2阻止事件的默认行为

有时我们需要阻止事件的默认行为, 比如: 一个超链接的默认行为就点击然后跳转到指定的页面。那么阻止默认行为就可以屏蔽跳转的这种操作,而实现自定义操作。

取消事件默认行为还有一种不规范的做法,就是返回 false。
link.onclick = function () {
alert('Lee');
return false; //直接给个假,就不会跳转了。
};


PS:虽然 return false;可以实现这个功能,但有漏洞;

必须写到最后,这样导致中间的代码执行后,有可能执行不到 return false;

return false 写到最前那么之后的自定义操作就失效了。所以,最好的方法应该是在最前面就阻止默认行为,并且后面还能执行代码。

//W3C,阻止默认行为
link.onclick = function (evt) {
evt.preventDefault();
alert('Lee');
};


//IE,阻止默认行为
link.onclick = function (evt) {
window.event.returnValue = false;
alert('Lee');
};


//跨浏览器兼容
function preDef(evt) {
var e = evt || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}


4.4.3contextmenu

上下文菜单事件:contextmenu,当我们右击网页的时候,会自动出现 windows 自带的菜单。那么我们可以使用 contextmenu 事件来修改我们指定的菜单,但前提是把右击的默认行为取消掉。

addEvent(window, 'load', function () {
var text = document.getElementById('text');
addEvent(text, 'contextmenu', function (evt) {
var e = evt || window.event;
preDef(e);
var menu = document.getElementById('menu');
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
menu.style.visibility = 'visible';
addEvent(document, 'click', function () {
document.getElementById('myMenu').style.visibility = 'hidden';
});
});
});


PS:contextmenu 事件很常用,这直接导致浏览器兼容性较为稳定。

4.4.4beforeunload

卸载前事件:beforeunload,这个事件可以帮助在离开本页的时候给出相应的提示, “离开”或者“返回”操作。

addEvent(window, 'beforeunload', function (evt) {
preDef(evt);
});


4.4.5mousewheel和DOMMouseScroll

鼠标滚轮(mousewheel)和 DOMMouseScroll,用于获取鼠标上下滚轮的距离。

addEvent(document, 'mousewheel', function (evt) { //非火狐
alert(getWD(evt));
});
addEvent(document, 'DOMMouseScroll', function (evt) { //火狐
alert(getWD(evt));
});
function getWD(evt) {
var e = evt || window.event;
if (e.wheelDelta) {
return e.wheelDelta;
} else if (e.detail) {
return -evt.detail * 30; //保持计算的统一
}
}


PS:通过浏览器检测可以确定火狐只执行 DOMMouseScroll。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript 事件