DOM事件模型
在0级DOM事件模型中,它只是简单的执行你为它绑定的事件,比如你为某个元素添加了一个onclick事件,当事件触发时,它只是去调用我们绑定的那个方法,不再做其他的操作。
在2级DOM事件模型中,就比较复杂一些,它将不再是单纯的调用一下自身绑定的事件就完事了,它还拥有机会去处理它的祖先节点,在DOM2级事件模型中,它有一个事件传播过程,分为3个阶段,从“事件捕获”Document开始来到“目标节点”再从“目标节点”冒泡回Document对象,举段代码
<div id="div">
<a href="javascript:;">DOM事件模型</a>
</div>
<script>
var div = document.getElementById("div");
var a = div.children[0];
document.onclick = function(){
console.log("document");
};
a.onclick = function(){
console.log("a");
};
div.onclick = function(){
console.log("div");
};
</script>
可以看到我只是点击了a元素,但是div和document绑定的事件也被触发了,这就是DOM2级和1级的区别,同时你也看到,它是先输出的a,而不是div和document,虽然说它有3个阶段,但浏览器默认是在冒泡阶段才执行的,如果不这样的话,我们点击a元素就会执行多次啦。
如果你想让浏览器在捕获阶段执行,那么就不能直接使用onclick添加事件了,而是要使用addEventListener添加事件,它的第三个参数就是用来设置在哪个阶段执行,具体可以看 http://www.runoob.com/jsref/met-element-addeventlistener.html
currentTarget
event.currentTarget获取到的是当前绑定事件的那个对象,也因为此原因,他获取到的常常和this一样,下面是一个示例:
<a href="javascript:;" id="a">evnet.currentTarget</a>
<script>
var a = document.getElementById("a");
a.onclick = function(event){
console.log("this:",this);
console.log("currentTarget:",event.currentTarget);
};
</script>
当我点击a标签时,this和currentTarget打印出来的都是a标签本身,如下图
既然如此这个currentTarget有啥用呢,这是一开始的想法,但随后发现不对,想到这个currentTarget始终获取到的是那个绑定事件的对象,但this却有很大的不同,因为this并不关心是谁绑定的它,它只关心是谁执行的它,因此如果再将上面那段代码改造改造,我们就会发现,它们真的是不一样的,代码如下:
var a = document.getElementById("a");
a.onclick = function(event){
(function(){
console.log("this:",this);
console.log("currentTarget:",event.currentTarget);
}());
};
效果如图
这也就是说,某些时候如果不能通过this来获取绑定事件的对象时,就可以使用event.currentTarget。
有些人认为event.currentTarget就是this,其实不然,容易把event.currentTarget当成this,主要原因就是,在事件处理器中,我们常常使用的是this,而不是event.currentTarget,至于为什么,反正我是因为从接触js开始,所看过的教程上都是那么用的,时间长了,竟然忘了一件事,event.currentTarget才是真的属于事件处理器的,而this不过是个冒牌货。
target
event.target获取到的是触发事件的那个元素。
网上常说的事件委派,就是通过event.target来实现的,所谓的事件委派,就是我并不给某个具体的东西添加事件,而通过给它的父辈添加事件,当我点击那个具体的元素时,父辈的事件会触发(因为有事件冒泡),而这时就需要用到evnet.target了,因为在父辈的事件中this并不指向当前点击的那个元素,但是event.target可以获取到是谁触发的当前事件。以下是一个示例
<ul id="ul">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
<script>
var ul = document.getElementById("ul");
ul.onclick = function(event){
console.log(event.target);
};
</script>
当我点击第二个li时,输出如下值
当然我们也可以直接给这三个li添加事件,但是那样的话就绑定了3次事件,如果有1万个元素,就绑定了1万次,而通过事件委派则只需要绑定一次。
最主要倒也不是说不能给li添加事件,而是如果这些li并不是事先添加的,而是通过后端返回的数据,再渲染的,那么要是我们再放回数据之前就给li添加事件,那么就会有问题,因为根本就不存在li元素,也就是说后添加的元素无法事先去添加事件,比如下面这段代码就有些问题。
<ul id="ul">
<li>1111</li>
</ul>
<script>
var ul = document.getElementById("ul");
var lis = ul.children;
for(var i=0;i<lis.length;i++){
lis[i].onclick = function(){
console.log(this);
};
}
var li = document.createElement("li");
li.innerText = "2222";
ul.appendChild(li);
</script>
当我点击第二个li时,什么都没有输出,如下图
因为第二个li是在for循环以后添加的,所有并没有给第二个li添加上事件。而如果是给ui添加事件,那就不一样了,代码如下
<ul id="ul">
<li>1111</li>
</ul>
<script>
var ul = document.getElementById("ul");
ul.onclick = function(event){
console.log(event.target);
};
var li = document.createElement("li");
li.innerText = "2222";
ul.appendChild(li);
</script>
不管我点击第几个li都可以正常的输出,如下图
不过因为event.target获取到的是最终触发这个事件的元素,所以在写代码的时候,我们经常需要加上判断,因为通过event.target获取到的不一定是我们想要的元素,比如下面这个例子
<ul id="ul">
<li>
<em>111</em>
</li>
</ul>
<script>
var ul = document.getElementById("ul");
ul.onclick = function(event){
console.log(event.target);
};
</script>
当我点击111的时候,获取到的是em标签,如下图
但我想要的是li,因此我们就得加上判断,我经常使用的一招就是,通过判断元素的标签名,代码如下
<ul id="ul">
<li>
<em>111</em>
</li>
</ul>
<script>
var ul = document.getElementById("ul");
ul.onclick = function(event){
var target = event.target;
if(!(target.tagName.toLowerCase()==="li")){
target = target.parentNode;
}
if(target.tagName.toLowerCase()==="li"){
console.log(target);
}
};
</script>
tagName可以获取到元素名,但是每个浏览器获取到的都有可能不同,有些浏览器获取到的是大写的标签名,有些浏览器获取到的是小写的标签名,因此在上面那段代码中,将标签名都转换成小写的,通过判断标签名来确定是不是我要的元素。
虽然这种判断可行,但仔细想想也能想到,这个方法,也是有缺陷的,如果DOM比较复杂,则容易判断错误,目前还没有想到更好的方法。
记住正是因为有了事件捕获和事件冒泡才有了event.target的用武之处。
relatedTarget
在event中有一个relatedTarget属性,它可以获取到和它相关的元素(通过谁来到这个元素上的,要到哪个元素上去),举个例子
<div id="box">
<p>新的起点,新的梦想。</p>
</div>
<script>
var box = document.getElementById("box");
box.onmouseout = function(event){
console.log(event.relatedTarget);
};
</script>
在onmouseout中relatedTarget可以获取到它要到哪个元素上去,相反在onmouseover中relatedTarget获取到的是从哪个元素来的,代码如下
<div id="box">
<p>新的起点,新的梦想。</p>
</div>
<script>
var box = document.getElementById("box");
box.onmouseover = function(event){
console.log(event.relatedTarget);
};
</script>
可以看到当我从html移入到div上时,relatedTarget获取到的是html,也就是它从html来到div的。
想起一句话:我从哪里来,又要到哪里去。
当第一次看到这个属性的时候,给我的感觉是,它肯定是很有用的,事实上也确实有些用处,如果细心的朋友,看上面的那个动画图,会发现一件事,onmouseover和onmouseout存在一个问题,离开或进入它的子元素事件也会被触发。大多数情况我们是不希望这样的,因此如果使用onmouseover或onmouseout时,最好判断一下,是否真的移出了盒子。
在元素中有一个contains方法,可以用来判断某个元素是否是它的子元素,如果是返回true,否则返回false,而以上问题我们就可以通过这个方法来写,代码如下
<div id="box">
<p>1111111
<em>新的起点,新的梦想。</em>
</p>
</div>
<script>
var box = document.getElementById("box");
box.onmouseout = function(event){
if(!this.contains(event.relatedTarget)){
console.log(this);
}
};
</script>
如果你只是想解决以上这个问题,那么大可不必这样写,因为在浏览器中,分别有两个和它们相同的事件,但它们并没有这个问题,分别是onmouseenter鼠标移入事件和onmouseleave鼠标移出事件。
需要注意的是relatedTarget属性只对onmouseout和onmouseover事件有用,虽然对onmouseenter和onmouseleave事件也有用,不过很少有人会那么去用,因为我发现relatedTarget属性除了用在解决上面那个问题以外,还真没发现有什么其他的用处。
注意点
需要注意一点,在普通函数中是不存在event对象的,只有在事件中才有,比如这么这段代码,如果使用event就会输出undefined。
(function(event){
console.log(event); //undefined
})();
自定义事件
官方提供了一些如onclick、onmouseover、onscroll等事件给我们使用,但难免也会有自己建立事件的需求,比如监听某个变量的变化,虽然听起来好像不太可能,但方法总是有的,我们不让使用者直接操作某个变量,而是提供一个方法给他操作,下面是一段实现思路。
<script>
var Foo = (function(){
var a = 10;
return {
get:function(){
return a;
},
set:function(value){
if(a!==value){
a = value;
this.change();
}
},
change:function(){
console.log("a的值有变化");
}
};
})();
Foo.set(9);
Foo.set(52);
</script>
以上我将变量a封死在函数作用域里面,让外部无法直接操作变量a,要操作就只能通过调用我事先设置的set方法,之所以要这样弄是因为我们是无法知道变量是什么时候改变了的,而如果让使用者操作我们提供事先提供的方法,那么我们就可以对其进行处理了。
以上并不是一个自定义事件,如果想要实现自定义方法,我们可以通过javascript提供的3个方法来弄,分别是document.createEvent()、event.initEvent()、element.dispatchEvent()。
- createEvent用来创建一个新的事件
- initEvent用来初始化事件
- dispatchEvent用来触发事件
具体可以看 http://www.w3school.com.cn/xmldom/met_event_initevent.asp
实现如下
<script>
var ev = document.createEvent("HTMLEvents");
ev.initEvent("changeA",false,false);
var Foo = (function(){
var a = 10;
return {
get:function(){
return a;
},
set:function(value){
if(a!==value){
a = value;
document.dispatchEvent(ev);
}
}
};
})();
document.addEventListener("changeA",function(){
console.log("a有变化",Foo.get());
});
Foo.set(555);
Foo.set(520);
</script>
以上也就是将set中的change方法改成dispatchEvent,用来触发changeA事件,如果你想要解绑这个事件,和你给onclick事件解绑是一样的。
IE attachEvent之坑
当我们通过attachEvent添加事件时,需要注意一件事,我们为它添加的事件函数,IE并没有将这个函数作为元素的方法调用,而是作为全局函数来调用,因此,它里面的this并不指向当前绑定的事件对象,而是window,并且event也不指向当前事件对象,看下面这段代码
<a href="javascript:;" id="a">addEventListener</a>
<script>
var a = document.getElementById("a");
a.attachEvent("onclick",function(){
console.log("this:",this);
console.log("currentTarget:",event.currentTarget);
});
</script>
当我点击a标签时,输出如下:
走进javascript——DOM事件的更多相关文章
-
JavaScript Dom 事件
JavaScript Dom 事件 对于事件需要注意的要点: // this标签当前正在操作的标签. this // event封装了当前事件的内容. even 常用事件 // 鼠标单击.触发事件 ...
-
JavaScript DOM 事件模型
JavaScript DOM 事件模型 JavaScript 是基于面向对象和事件驱动的一门语言,事件模型是 DOM 中至关重要的内容,理解事件驱动机制.事件反馈.事件冒泡.事件捕获以及事件委托能帮助 ...
-
JavaScript DOM事件模型
早期由于浏览器厂商对于浏览器市场的争夺,各家浏览器厂商对同一功能的JavaScript的实现都不进相同,本节内容介绍JavaScript的DOM事件模型及事件处理程序的分类. 1.DOM事件模型.DO ...
-
JavaScript DOM&ndash;事件操作
事件 注册事件 给元素添加事件,为注册事件或者绑定事件 注册事件两种方式 传统方式 监听事件方式 事件监听 addEventListener() 事件监听 (IE9以上) eventTarget.ad ...
-
JavaScript -- DOM事件
什么是事件 事件就是文档或浏览器窗口中发生的一些特定的交互瞬间.比如你在网页上见到的鼠标点击一个按钮,按钮的颜色发生了变化,就是因为这个标签绑定了点击事件 鼠标事件 onload:页面加载时触发 on ...
-
JavaScript DOM事件对象的两个小练习 | 学习内容分享
Event 对象 Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态. 事件通常与函数结合使用,函数不会在事件发生前被执行! 本文用于记录个人学习过程 ...
-
javascript DOM事件总结
1 <html> 2 <title>事件</title> 3 <meta charset="utf-8"/> 4 <body& ...
-
DOM事件机制进一步理解
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
-
JavaScript DOM编程基础精华01(DOM入门,DOM模型和获取页面元素,事件,window对象的方法)
DOM入门 DOM就是Html页面的模型,将每个标签都做为一个对象,JavaScript通过调用DOM中的属性.方法就可以对网页中的文本框.层等元素进行编程控制.比如通过操作文本框的DOM对象,就可以 ...
随机推荐
-
MVVM 在使用 ItemsSource 之前,项集合必须为空
今天在做ListBox和Combobox绑定的时候,都出现过“在使用 ItemsSource 之前,项集合必须为空”的错误. Combobox比较简单,代码如下: <ComboBox x:Nam ...
-
sql 游标例子 根据一表的数据去筛选另一表的数据
sql 游标例子 根据一表的数据去筛选另一表的数据 DECLARE @MID nvarchar(20)DECLARE @UTime datetime DECLARE @TBL_Temp table( ...
-
Android开发艺术探索》读书笔记 (8) 第8章 理解Window和WindowManager
第8章 理解Window和WindowManager 8.1 Window和WindowManager (1)Window是抽象类,具体实现是PhoneWindow,通过WindowManager就可 ...
-
史上最强Android 开启照相或者是从本地相册选中一张图片以后先裁剪在保存并显示的讲解附源码
整个程序的布局很简单 只在一个垂直方向上的线性布局里面有俩个按钮(Button)和一个显示图片的控件(ImageView)这里就不给出这部分的代码了 1.是打开系统的相册 Intent alb ...
-
2017.4.28 KVM 内存虚拟化及其实现
概述 KVM(Kernel Virtual Machine) , 作为开源的内核虚拟机,越来越受到 IBM,Redhat,HP,Intel 等各大公司的大力支持,基于 KVM 的开源虚拟化生态系统也日 ...
-
Nginx配置跨域请求“Access-Control-Allow-Origin”
当出现403跨域错误的时候 No 'Access-Control-Allow-Origin' header is present on the requested resource,需要给Nginx服 ...
-
java常见编码
摘自:http://www.cnblogs.com/yaya-yaya/p/5768616.html 红色 主要点 灰色 内容 绿色 知识点 橘色 补充内容 几种常见的编码格式 ...
-
Quartz.NET开源作业调度框架系列(二):CronTrigger-转
CronTriggers比SimpleTrigger更加的灵活和有用,对于比较复杂的任务触发规则,例如"每个星期天的晚上12:00"进行备份任务,SimpleTrigger就不能胜 ...
-
SrervletContext和文件下载
ServletContext对象 生命周期(从生命周期可以看出这个是个全局对象) 项目启动的时候创建 项目关闭的时候销毁 概念:代表整个web应用,可以和程序的容器(服务器)来通信. 获取 通过req ...
-
成员函数的重载&;amp;&;amp;隐藏&;amp;&;amp;覆盖
/* *成员函数的重载,覆盖,隐藏 *重载: *1.同样的范围(在同一个类中) *2.函数名同样 *3.參数不同 *4.virtualkeyword可有可无 *覆盖是指派生类覆盖基类的函数,特征是: ...