你真的了解DOM事件么?

我们大家都知道,人与人之间的交流可以通过语言,文字,肢体动作,面部微表情等,但是你知道Javascript和HTML之间是通过什么进行交互的么?你又知道Javascript和HTML之间是如何进行交互的么?如果你不是那么清楚,可以看这篇文章。。。

前面的话:

这篇博文由浅入深,先介绍两种事件流,然后介绍常见的事件处理程序以及它们之间的差异(涉及到一个简单的兼容的处理函数),后面还会针对事件对象进行深入学习,最后则是通过DOM事件实现的小案例。

事件流:

关于事件流,简单的来说就是:事件流描述的是从页面中接受事件的顺序。下面是《Javascript高级程序设计》里面的说法:

当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆。两家公司的开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,那么同时你也单击了按钮的容器元素,甚至整个页面。

事件流描述的是从页面中接受事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了两个截然相反的事件流概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。

事件冒泡

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。看下面的例子

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件流</title>
</head>
<body>
<input type="button" id="btn" value="" />
</body>
</html>

点击按钮,那么这个click事件会按照这样传播:
<input-->body-->html-->document

事件捕获

Netscape团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

针对上面同样的例子,点击按钮,那么此时click事件会按照这样传播:
document-->html-->body-->input

虽然事件捕获是Netscape唯一支持的事件流模型,但IE9、Safari、Chrome、Opera和Firefox目前也都支持这种事件流模型。但由于老版本的浏览器不支持,因此很少有人使用事件捕获。

事件处理程序

HTML事件处理程序

HTML事件处理程序(现在不建议使用了):事件直接加在HTML代码中,例如:

<input type="button" id="btn" value="HTML事件处理程序" onclick="alert('HTML事件处理程序')" />

或者:

<input type="button" id="btn1" value="HTML事件处理程序" onclick="demo1()" />
function demo1(){
alert("HTML事件处理程序");
}

缺点:HTML和js代码高耦合,如果修改,就要修改两个地方--HTML元素内和script函数。

DOM0级事件处理程序

DOM0级事件处理程序,是一种较传统的方式:把一个函数赋值给一个事件的处理程序属性。例如:

var btn2 = document.getElementById("btn2");
btn2.onclick = function(){
alert("DOM0级事件处理程序");
}

现在用得比较多,优势是简单(只需要考虑JS不考虑HTML)且跨浏览器。如果想取消点击事件,将其设为null即可。

var btn2.onclick = null;//这时点击不会弹出窗口,表示已经取消了该点击事件

下面是我学习时候的一些思考:

思考一:DOM0级事件是否可以同时添加多个不同事件处理方法

var btn2 = document.getElementById("btn2");
btn2.onclick = function(){
console.log("DOM0级事件处理程序-click-01");
}
btn2.onmouseover = function(){
console.log("DOM0级事件处理程序-mouseover");
}

结果:鼠标经过,控制台出现:DOM0级事件处理程序-mouseover;鼠标点击,控制台出现:DOM0级事件处理程序-click-01。
说明:DOM0级事件可以同时添加多个不同事件处理方法

思考二:DOM0级事件是否可以同时添加多个相同事件处理方法

我们在前面的基础上添加如下代码:

btn2.onclick = function(){
console.log("DOM0级事件处理程序-click-02");
}

结果:鼠标经过,控制台出现:DOM0级事件处理程序-mouseover;鼠标点击,控制台出现:DOM0级事件处理程序-click-02。(在这里你可能以为会出现DOM0级事件处理程序-click-01和DOM0级事件处理程序-click-02)
说明:DOM0级事件不可以同时添加多个相同事件处理方法(这样说好像不太严格,大概意思就是直接忽略了前面的onclick,只显示最后的onclick的结果。
那么这里就设下一个疑问:我们应该如何让DOM同时绑定多个相同事件呢???我们接着往下看

DOM2级事件处理程序

DOM2级事件处理程序定义了两个方法--用于处理指定和删除事件处理程序的操作:

  addEventListener()添加事件监听程序

  removeEventListener()移除事件监听程序

  三个参数设置;事件名称,处理方法(函数),布尔值--false:表示在冒泡阶段调用事件处理程序(可以最大限度的兼容浏览器),一般设为false;true表示在捕获阶段调用事件处理程序

来看下面的例子:

var btn3 = document.getElementById("btn3");
btn3.addEventListener('click',function(){
console.log("DOM2级事件处理程序-click-01");
},false)
//DOM2级事件处理程序-click-01

与DOM0级不同的是,我们移除需要使用removeEventListener(),代码如下:

var btn3 = document.getElementById("btn3");
btn3.addEventListener('click',function(){
console.log("DOM2级事件处理程序-click-01");
},false)
btn3.removeEventListener('click',function(){
console.log("DOM2级事件处理程序-click-移除成功");
},false)
//DOM2级事件处理程序-click-01

在这里,你很有可能和我想的一样,DOM2级事件处理程序已经移除完了。但是结果是DOM2级事件处理程序-click-01依然还在,按照理论上来说不应该是被移除了么?但是为什么还在?

原因:通过addEventListener方法添加的事件处理程序只能用removeEventListener来移除,移除时传入的参数与添加处理程序时使用的参数相同。这就意味着通过addEventListener添加的匿名函数将无法移除。真的是这样么?我们继续来看例子:

var btn3 = document.getElementById("btn3");
var handler = function demo3(){
alert("DOM2级事件处理程序");
}
btn3.addEventListener('click',handler,false)
//在不加后面的代码的时候弹框显示DOM2级事件处理程序
btn3.removeEventListener('click',handler,false)
//加了removeEventListener后点击,没有弹框,说明该点击事件已经被移除了

接下来我们同样思考几个问题:
思考一:DOM2级事件是否可以同时添加多个不同事件处理方法

var btn3 = document.getElementById("btn3");
btn3.addEventListener('click',function(){
console.log("DOM2级事件处理程序-click-01");
},false)
btn3.addEventListener('mouseover',function(){
console.log("DOM2级事件处理程序-mouseover");
},false)

结果:鼠标经过,控制台出现:DOM2级事件处理程序-mouseover;鼠标点击,控制台出现:DOM2级事件处理程序-click-01。
说明:DOM2级事件可以同时添加多个不同事件处理方法

思考二:DOM2级事件是否可以同时添加多个相同事件处理方法

var btn3 = document.getElementById("btn3");
btn3.addEventListener('click',function(){
console.log("DOM2级事件处理程序-click-01");
},false)
btn3.addEventListener('mouseover',function(){
console.log("DOM2级事件处理程序-mouseover");
},false)
btn3.addEventListener('click',function(){
console.log(this.id);
},false)

结果:鼠标经过,控制台出现:DOM2级事件处理程序-mouseover;鼠标点击,控制台出现:DOM2级事件处理程序-click-01和btn3。
说明:DOM2级事件可以同时添加多个不同事件处理方法,同时在这里可以用this引用被触发的元素。

注意:
  1、要处理的事件名(事件名去掉“on”,如点击事件需写成click);
  2、作为事件处理程序的函数名(函数不需要带括号,只要函数名或者直接写函数体也可以);
  3、布尔值(true表示使用事件捕获流,false表示使用事件冒泡流,一般使用false兼容各种浏览器)。
  4、通过addEventListener添加的事件只能通过removeEventListener来移除;DOM 0级和DOM 2级事件可以在一个DOM上添加多个事件,按顺序执行
  5、DOM2级事件处理程序适用于:IE9、Firefox、Safari、Chrome和Opera

思考:那么如果我们想要兼容低版本的IE该怎么办呢?请继续往下阅读

IE事件处理程序

(声明:IE事件处理程序的代码没有进行测试)

IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent(),这两个方法接受相同的两个参数:事件处理程序名称与事件处理函数程序函数(因为IE8及更早版本只支持时间冒泡,所以没有第三个参数)。下面我们来看代码:

var btn4 = document.getElementById("btn4");
btn4.attachEvent('onclick',function(){
console.log("IE事件处理程序-onclick-01");
});

这里需要注意的是,第一个参数是onclick,而不是click。另外与DOM0级中的不同在于作用域,看下面的代码:

var btn4 = document.getElementById("btn4");
btn4.attachEvent('onclick',function(){
console.log(this === window); //true
});

其他需要注意的有:
1、attachEvent方法也可以为一个元素添加一个或多个事件处理程序,当添加多个相同的事件处理程序的时候,这些事件处理程序不是以添加它们的顺序而执行,而是以相反的顺序被触发。比如下面这个例子:

var btn4 = document.getElementById("btn4");
btn4.attachEvent('onclick',function(){
console.log("hello");
});
btn4.attachEvent('onclick',function(){
console.log("World");
});

这段代码用DOM0级的方法得到的结果为World,用DOM2级的方法得到结果为先是hello然后是World,而在这里的结果为先是World然后是hello。

2、detachEvent()方法和DOM2级中移除的方法一样,同样也需要注意中间添加的匿名函数不能被移除,这里就不再举例说明了。

从HTML事件处理程序到DOM0级事件处理程序,再到DOM2级处理程序,还有IE事件处理程序,我们在使用的时候为了让用户体验最佳,我们该让我们的代码兼容各种浏览器,那么我们有什么好的方法让我们的代码兼容到各种浏览器中呢?

跨浏览器的事件处理程序

为了解决跨浏览器的问题,我们不得不集中的把前面几种方法柔和在一起,然后就出现了跨浏览器的事件处理程序。(我的思路如下)

1、先考虑添加事件的情况,在添加的时候,我们要知道给谁添加?添加的是什么事件?然后事件触发的是什么函数?
        给谁添加?当然是我们的元素了,因为还不知道,这里我们先设置为element
        添加的是什么事件?click?onclick?onmouseover?我们也还是不知道,先设置为type吧
        添加什么函数?前面的例子中有用到过handler这里也先就用这个

function(element, type, handler) {……}

2、我们要判断我们的浏览器是否支持相关的操作,这里我们可以用if-else来判断

if(element.addEventListener) {
//DOM2级事件处理程序
} else if(element.attachEvent) {
//IE事件处理程序
} else {
//DOM0级事件处理程序
}

3、针对DOM2级事件处理程序、IE事件处理程序和DOM0级事件处理程序它们有相似的地方,然后我们通过前面可以知道,它们对于事件的处理有点不和谐,我们该怎样呢?很明显,添加"on"比去掉"on"更简单,所以比如点击事件的type应该为click,然后在需要用onclick的地方,使用'on'+type,然后各自保留各自的特点    
DOM2级事件处理程序:element.addEventListener(type, handler, false);
IE事件处理程序:element.attachEvent('on' + type, handler);
DOM0级事件处理程序:element['on' + type] = handler;

4、到这里基本的添加情况以完成,接下来用同样的方法来考虑移除事件的情况(步骤省略)

5、然后分别给添加事件函数和移除事件函数进行命名addHandler和deleteHandler,并封装起来,得到我们的最终效果:

var eventUtil = {
addHandler: function(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
} else if(element.attachEvent) {
element.attachEvent('on' + type, handler);
} else {
element['on' + type] = handler;
}
},
deleteHandler: function(element, type, handler) {
if(element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if(element.detachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
}
}

事件对象

DOM中的事件对象

  前面我们已经封装好一个函数了,我们后面的操作直接引用前面的那个函数。说到事件对象,我们先不得不说事件对象的两个重要属性type和target
  其中type是获取事件的类型,而target是获取事件的目标。直接看代码:

var btn = document.getElementById("btn");
eventUtil.addHandler(btn,'click',showMes);
function showMes(event){
alert(event.type);//结果是弹出click
}

结果是弹出click,说明了该事件的类型是点击事件

var btn = document.getElementById("btn");
eventUtil.addHandler(btn,'click',showMes);
function showMes(event){
alert(event.target);//结果是弹出[object HTMLInputElement]
}

结果是弹出[object HTMLInputElement],说明该事件的目标是input元素

下面我们运行一段代码,来看一个有趣的现象:

<div id="box">
<input type="button" id="btn" value="DOM中的事件对象" />
<a href="www://baidu.com" id="go">百度</a>
</div> function showMes(event){
alert(event.target.nodeName);
} function showBox(){
alert("这是放盒子的box")
} var btn = document.getElementById("btn");
var box = document.getElementById("box"); eventUtil.addHandler(btn,'click',showMes);
eventUtil.addHandler(box,'click',showBox);

运行结果:鼠标点击按钮,分别弹出了“INPUT”和“这是放盒子的box”,我们只点击了按钮,为什么按钮所在的div也会显示呢?查资料知道这是因为事件冒泡机制导致的。

那么我们该如何阻止事件冒泡呢?

我们的事件对象给我们提供了一种方法:通过stopPropagation()方法来实现,代码示例如下:

function showMes(event){
alert(event.target.nodeName);
event.stopPropagation();//阻止事件冒泡
} function showBox(){
alert("这是放盒子的box")
} var btn = document.getElementById("btn");
var box = document.getElementById("box"); eventUtil.addHandler(btn,'click',showMes);
eventUtil.addHandler(box,'click',showBox);

光知道阻止事件冒泡行为还不行我们还需要知道的一种方法:阻止事件默认行为-->preventDefault()方法

比如我们知道的a标签有一个默认行为就是跳转到目标地址,如果我们想要阻止这种行为我们就可以使用该方法,代码如下:

<a href="www://baidu.com" id="go">百度</a>

var go = document.getElementById("go");
function showGo(event){
event.stopPropagation();//阻止事件冒泡
event.preventDefault();//阻止事件的默认行为
}
eventUtil.addHandler(go,'click',showGo);

运行结果是,无论怎样点击,都跳转不到百度页面。
在a标签里面阻止事件默认行为还有一种方法就是直接在a标签里面添加,比如<a href="javascript:;">aa</a>

补充:在需要通过一个函数处理多个事件时,可以使用type属性,比如:

var btn = document.getElementById("btn");
var handler = function(event){
switch(event.type){
case"click":
alert("Clicked");
break; case"mouseover":
event.target.style.backgroundColor = "red";
break; case"mouseout":
event.target.style.backgroundColor = "";
break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

IE中的事件对象

  【type属性】用于获取事件类型
  【srcElement属性】用于获取事件的目标(DOM中用的是target),所以,可以使用

event=event || window.event;(考虑IE8以前的版本)
var ele=event.target event.srcElement;(考虑非IE与IE)

  【cancelBubble属性】用于阻止事件冒泡,设为true表示阻止冒泡,false表示不阻止冒泡。
  【returnValue属性】用于阻止事件的默认行为,设为false表示阻止。
  这里只介绍IE事件对象的常用属性,关于它的具体例子就不做介绍了

跨浏览器的事件对象:

搞清楚前面的问题,下面的一段代码看起来就简单了。它直接将前面提到的所有方法和属性一起封装起来,并保存到一个单独的js文件中,命名为event.js,其代码如下:

var eventUtil = {
// 添加句柄
addHandler: function(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
} else if(element.attachEvent) {
element.attachEvent('on' + type, handler);
} else {
element['on' + type] = handler;
}
},
// 删除句柄
removeHandler: function(element, type, handler) {
if(element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if(element.detachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
//获取事件
getEvent: function(event) {
return event ? event : window.event;
},
//获取事件类型
getType: function(event) {
return event.type;
},
//获取元素
getElement: function(event) {
return event.target || event.srcElement;
},
//阻止事件默认行为
preventDefault: function(event) {
if(event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
//阻止事件冒泡
stopPropagation: function(event) {
if(event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
}

我们可以用如下代码来测试点击a事件

window.onload=function(){
var go=document.getElementById('go'),
box=document.getElementById('box'); eventUtil.addHandler(box,'click',function(){
alert('我是整个父盒子');
}); eventUtil.addHandler(go,'click',function(e){
//e=eventUtil.getEvent(e);
e=e || window.event;
alert(eventUtil.getElement(e).nodeName);
eventUtil.preventDefault(e);
eventUtil.stopPropagation(e);
}); }

事件类型

因为事件类型比较多,这里主要通过两个具体的案例来展示,不做详细介绍

案例一:用鼠标或键盘事件来控制抽奖。

效果截图:

查看地址:http://sandbox.runjs.cn/show/0pibyaes

案例二:模仿扣扣登陆时的拖曳效果(主要学习其中如何实现拖曳效果的思维)

效果截图:

查看地址:http://sandbox.runjs.cn/show/7mhb7pfw

更多参考资料:

  参考书籍:《Javascript高级程序设计(第三版)》

  参考博客: https://my.oschina.net/u/1403185/blog/184960

    http://www.cnblogs.com/luozhihao/p/5934935.html

    http://www.cnblogs.com/xiaohuochai/p/5867195.html

你真的了解DOM事件么?的更多相关文章

  1. 走进javascript——DOM事件

    DOM事件模型 在0级DOM事件模型中,它只是简单的执行你为它绑定的事件,比如你为某个元素添加了一个onclick事件,当事件触发时,它只是去调用我们绑定的那个方法,不再做其他的操作. 在2级DOM事 ...

  2. Javascript高级编程学习笔记(71)—— 模拟事件(1)DOM事件模拟

    事件,指的是网页中某个特定的交互时刻 一般来说事件由浏览器厂商负责提供,一般由用户操作或者其它浏览器功能来触发 但是有一类特殊的事件,那就是由我们开发人员通过JS触发的事件 这些事件和浏览器创建的事件 ...

  3. 彻底理解H5的DOM事件

    我们大家都知道,人与人之间的交流可以通过语言,文字,肢体动作,面部微表情等,但是你知道Javascript和HTML之间是通过什么进行交互的么?你又知道Javascript和HTML之间是如何进行交互 ...

  4. DOM 事件深入浅出(二)

    在DOM事件深入浅出(一)中,我主要给大家讲解了不同DOM级别下的事件处理程序,同时介绍了事件冒泡和捕获的触发原理和方法.本文将继续介绍DOM事件中的知识点,主要侧重于DOM事件中Event对象的属性 ...

  5. DOM 事件深入浅出(一)

    在项目开发时,我们时常需要考虑用户在使用产品时产生的各种各样的交互事件,比如鼠标点击事件.敲击键盘事件等.这样的事件行为都是前端DOM事件的组成部分,不同的DOM事件会有不同的触发条件和触发效果.本文 ...

  6. DOM事件

    在慕课网上学习了DOM事件探秘课程,特此整理了一下笔记. 慕课网DOM事件探秘课程地址:http://www.imooc.com/learn/138 事件 是文档或浏览器窗口中发生的特定的交互瞬间.[ ...

  7. 理解DOM事件流的三个阶段

    本文主要解决两个问题: 1.什么是事件流 2.DOM事件流的三个阶段 事件流之事件冒泡与事件捕获 在浏览器发展的过程中,开发团队遇到了一个问题.那就是页面中的哪一部分拥有特定的事件? 可以想象画在一张 ...

  8. [DOM Event Learning] Section 4 事件分发和DOM事件流

    [DOM Event Learning] Section 4 事件分发和DOM事件流 事件分发机制: event dispatch mechanism. 事件流(event flow)描述了事件对象在 ...

  9. dom事件与event对象总结

    1 事件:就是文档或浏览器窗口中发生的一些特定的交互瞬间.    tips:js和xhtml的交互是通过当用户或浏览器操作网页时发生的事件来处理的. 1.1 事件流:即事件的顺序.        事件 ...

随机推荐

  1. NLP常用工具

    1.统计类工具:可参见[统计学习常用Python扩展包] 2.linux自带工具:可参见[[整理]Linux常用文本处理命令] 3.简繁转换工具:opencc Open Chinese Convert ...

  2. 参加MVP OpenDay 和2015 MVP Community Camp社区大课堂

    微软MVP Openday 1月30日在北京召开,到时全国上百位 MVP 专家将齐聚北京.当然还有亚太的其他国家地区的MVP 也会来北京,1月31日微软 MVP 项目组主办的年度微软技术社区分享大会- ...

  3. 浅谈SQL Server数据库分页

    数据库分页是老生常谈的问题了.如果使用ORM框架,再使用LINQ的话,一个Skip和Take就可以搞定.但是有时由于限制,需要使用存储过程来实现.在SQLServer中使用存储过程实现分页的已经有很多 ...

  4. Hibernate 3.3.2 文档翻译 Day01

    Hibernate 3.3.2 文档翻译 翻译人:微冷的雨 第一次书写:2015年11月29日 本人呕心沥血之作,请细心阅读领悟! Day01-1.1 项目描述 微冷的雨翻译:例如,我们将要建立一个可 ...

  5. Node.js开发者最常范的10个错误

    目录 前言 1 不使用开发工具 1.1 自动重启工具 1.2 浏览器自动刷新工具 2 阻塞event loop 3 频繁调用回调函数 4 圣诞树结构的回调(回调的地狱) 5 创建一个大而完整的应用程序 ...

  6. C语言 · 送分啦

    问题描述 这题想得分吗?想,请输出"yes":不想,请输出"no". 输出格式 输出包括一行,为"yes"或"no". ...

  7. 2017预防bug的重要性

    Bug,中文名缺陷.一个让软件测试员兴奋,让开发人员头疼的词.来源二次大战期间,一个称为"马克二型"的计算机,由于天气过热,硬件跟不上导致死机.最后发现是因为飞蛾,被继电器电死,将 ...

  8. jQuery源码分析系列(34) : Ajax - 预处理jsonp

    上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处 ...

  9. sizzle分析记录:词法分析器(tokenize)

    词法分析器(tokenize)? 词法分析器又称扫描器.词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用. sizzle引入了tokenize这个概念,意义? ...

  10. JavaScript 中的类方法,对象方法,Prototype方法

    <script type="text/javascript"> function baseClass() { this.showMsg = function() { a ...