厉害了我的哥——你是如此简单我却将你给遗忘
放假前再看某文档,里边提到两个我既熟悉又陌生的概念target、currentTarget,说他熟悉我曾经看到过这两个事件对象的异同处,说他陌生吧?很不巧。正要运用了,吾才发现吾压根就没记住。。。其实要讲清楚target、currentTarget,我们就不得不说一下事件流这个概念,而要言明事件流,我们也要晓得一下神马是事件以及一些周边生态概念,这三个概念是必须放在一块说,我们才能对能对这部分的知识有很好的理解滴,网上信息太零散,于是乎就有了这篇算我复习的博文吧!大牛略过,如我般的白菜们阅读完本文你将可以了解如下概念:
神马是事件,事件三种写法html事件处理程序、dom0级、dom2级
事件流是什么鬼及其运用和使用上的坑~
target、this、currentTarget的区别
厉害了我的哥——事件
我们先聊聊事件,首先我们必须明确一点,事件通常与函数挂钩。要聊事件这个名词,我先扯下js,我们都知道js由ECMA、DOM、BOM组成,ECMA为js的基本语法,如果你实在不知道是啥,可以简单的理解为他规定了循环是用for关键字,而采用var来定义一个变量,诸如此类;BOM是浏览器对象模型的缩写,即用我们的js来操作浏览器;DOM是文档对象模型的缩写,那啥子是文档呢?简单可以这样理解——我们的html标签在被浏览器渲染的时候会生成一棵标签树,我们可以把这颗由标签构成的树别名称之为文档树或者文档,而每一个标签在js中都可以被抽成一个对象,so,到此你应该已了然于胸了吧?原来文档对象模型可以简单的理解为html标签文档在js中的对象映射,也就是有了dom这个概念,我们可以在js中操作html的元素咯,如图所示:
<html>
<head>
</head>
<body>
<div>
</div>
</body>
</html>
当我们在源文件中写下如上的代码,浏览器会解析成如图所示
如果把浏览器当作最顶层容器,抽象成文字可以理解为
window-》document-》html-》head/body-》其他元素
这个层级关系就可以理解为文档树了,现在我们可以得出如下几个概念:
- 凡出现在html中的标签被浏览器解析的时候都会被挂载到dom树上。
- dom树上的每个标签都是一个独立的对象,既然是对象就会有属性和方法,上文也提到过事件通常与方法不离不弃,所以这里我们就会晓得一个基本常识了,事件一般会和标签和方法挂钩,嗯是的!我们通常在标签上注册一个事件,当事件满足条件被触发以后就会执行我们早些预定义好的方法。
- 事件的概念就是:让html在某些特定条件下执行了一段js脚本,某些特定的条件就是如单击,双击,鼠标经过等,这些条件以属性的形式呈现在html代码中,而触发执行的js脚本则通常为方法,如代码:
<div onclick = "fn()"></div>
<script>
funcrion fn(){
alert("我被单击了");
}
</script>
onclick为某些条件的事件句柄,fn为事件满足条件后要执行的函数,以上只是事件的一种写法,不足以应付开发,为此我们还需要了解下常见事件的几种写法:
这里我们需要了解三种,分别为最原始的html混合事件写法,dom0级写法、dom2级写法:
- 最原始的写法:和html混合在一起写,缺点是代码高冗余,且无法添加多个事件处理函数如上文对事件的举例则为典型的**html事件处理程序*写法
-
dom0级:将html代码和js代码分离,且支持匿名函数,可以看到完美的改进了1的冗余缺憾,所有的事件相关操作都在js中完成
//html代码 <div id="a"></div> //js代码 var div1 = document.getElementById("a"); a.onclick=function(){ alert("我被单击了"); }
-
dom2级:ie使用attachEventListener其他非ie使用addEventListener,可以支持绑定多个事件,瞧吧,又一个缺憾被完美解决了~,而且dom2级还可以自定义事件流,好,到这里大家留个心,这是第二部分我们要介绍的重点。牢记这个名词于心哦~
-
dom3级:对事件进行了更广而全的分类,请大家自行查阅。
厉害了我的哥——事件流
接上,事件我们已经晓得是什么鬼了,现在多了个流,组个词人流,水流,流向,额貌似靠近点它的本意了,总有点排队方向的感脚,没错,很正确的第n感。事件流官网定义:描述的是从页面中接收事件的顺序,那~~~是谁从页面接收事件的顺序呢?接收的顺序有啥子呢?接收的范围呢?
咳咳,回答一下上边的问题呗,谁接收?你说呢?一点头绪还没有的童鞋建议重新看下上文吧,接收的顺序和范围,噗,这是接下来要说道说道的了:
接收顺序(事件流方式),需要我们了解:
1. 事件捕获:什么叫捕获,其实不用扯那么多一句话从最不具体的到最具体的
2. 事件冒泡:什么叫冒泡,正好和上面相反,从最具体的到最不具体的
3. 为什么会有两种事件流方式呢?历史原因,ie提出的是事件冒泡,而w3c提出的是事件捕获。
4. 现代浏览器高级了,那。。。。。嗯,我知道你要问啥,你一定要问浏览器内部是如何解析这两种事件流的,它的执行顺序:事件捕获-》目标阶段-》事件冒泡,一句话就是先捕获后冒泡
捕获和冒泡的范围呢?也就是接收事件的范围呢? 哈哈,这个上面其实我已经说过了:
window-》dowument-》html-》head/body-》div或其他元素
注意昂,现在的浏览器都是从window开始滴,不管冒泡还是捕获其的传递范围一定是父子关系,也就是外层到内层,或者从内层到外层。假设我们给div添加一个事件方法,如果是事件捕获的话是从window开始,因为他是最不具体的元素,如果是冒泡的话则是从div开始,所以这里的具体不具体是相对于添加事件的元素而言的,说到这里,大家一定要晓得,我们要使用事件流通常要配合dom2级事件去写,要是读到这里你还不了解这个,那宝宝就让你气死鸟~哦,还要知道的一点我们现开发中多使用的事件流通常是冒泡,嗯这点随了ie了,但是要注意现在的主流浏览器默认就是冒泡,但通常我们在使用addEventListener这个方法的时候他的第三个参数还是会显示的指定为false,即为不捕获**。
防着有人不理解,我多解释几句呗,捕获和冒泡的方式决定着浏览器何时去处理这个事件,如果我把事件声明在冒泡阶段处理,那么在捕获阶段即使捕获到了这个事件,事件也是不会被执行滴,这就是他的一个重要运用,这点也是经常被大家混淆和忽略的地方,但恰好也是它最好玩的地方。随意给大家从网上找两张万年错且没什么卵用的图,说他错是因为现在的事件范围是从window开始的,结果网上的很多图都是画到了document,如下图:
冒泡流程图
捕获流程图
事件流使用上最好玩的地方,也正是其最大的坑,大家看如下例子,
<div id="one">
<div id="two">
<div id="three"></div>
</div>
</div>
one.addEventListener('click',function(e){
console.log('one');
},false);
two.addEventListener('click',function(e){
console.log('two');
},false);
three.addEventListener('click',function(e){
console.log('three');
},true);
使用的是dom2级中的非ie方法,从代码中可以看到one和two为冒泡,three为捕获,所以事件的执行结果就会被改变,我相信很多童鞋若不看小弟这篇软文直接晕菜了吧?但是看到这里的你,一定会有一种say爽的感觉,当我们点击最里面的div的时候,打印数序应该为three->two->one。一句话总结:现代的浏览器对事件的处理方式为先捕获后冒泡,只要有事件就会按这个顺序执行,雷打不动滴,雷打不动滴。dom2级决定的只是只是只是在事件流的哪个时机去执行处理函数而已而已。
温故而知新啊~上文说到ie使用attachEventListener其他非ie使用addEventListener,且dom2级可以添加多个事件,我们都知道ie使用的是是冒泡也就是从具体到不具体,而非ie为捕获也就是从不具体到最具体,当我们要绑定多个事件处理函数的时候,事件处理函数的顺序ie和非ie在默认情况下是不一致的,因为他们两个的事件流默认情况下是正好相反的,这在不指定事件流参数的情况下确实是个坑~
厉害了我的哥——target、this、currentTarget的区别
现在终于到了target、this、currentTarget
先诉重点理论:
1. target:触发事件的某个具体对象,只会出现在事件流的目标阶段(谁触发谁命中,所以肯定是目标阶段)
2. currentTarget:绑定事件的对象,恒等于this,可能出现在事件流的任意一个阶段中
3. 通常情况下terget和currentTarget是一致的,我们只要使用terget即可,但有一种情况必须区分这三者的关系,那就是在父子嵌套的关系中,父元素绑定了事件,单击了子元素(根据事件流,在不阻止事件流的前提下他会传递至父元素,导致父元素的事件处理函数执行),这时候currentTarget指向的是父元素,因为他是绑定事件的对象,而target指向了子元素,因为他是触发事件的那个具体对象,如下代码和截图所示:
<div id="one">
<div id="three"></div>
</div>
one.addEventListener('click',function(e){
console.log(e.target); //three
console.log(e.currentTarget); //one
},false);