- 给一个孩子元素绑定了click事件,也给爸爸元素绑定了click事件,你点击了这个孩子元素,你怎么知道你到底要点击孩子还是爸爸(因为孩子是爸爸的一部分,点击了孩子不就是点击了爸爸)
- 所以,点击了孩子就把爸爸、爷爷、曾爷爷的点击事件一起触发了吧
- 同理,孩子也有孩子,也就是孙子。你想点击了孩子,但是要是一不小心点到孙子身上,孙子因为是孩子的一部分,也算点到了孩子,就会触发孩子的事件监听函数。所以你给孩子绑定了函数,但是触发函数的可能是孙子。
- 所以,触发(target)监听函数的不一定是绑定元素,还可能是绑定元素的儿子
事件
定义
- 事件是在编程时系统内发生的动作或者发生的事情
- 事件是要绑定在元素上的。比如给一个div元素绑定一个鼠标悬浮事件,给一个ol元素绑定鼠标单击事件。
- 可以使用事件监听函数(也叫事件处理程序、侦听器)来监听事件,以便事件发生时执行相应的代码
事件类型举例(全部事件类型请看MDN文档)
- 单击
click - 双击
dblclick - 鼠标放置
mouseenter - 表单内容发生变化
- 拖拽
- 页面滚动
- 触发/失去焦点
- 键盘按下
- 提交表单
DOM事件流
你想想,如果我按了键盘上的空格,是不是也相当于按了整个键盘?我按了空格的右下角的英文,是不是相当于按了空格。
定义
事件流描述的是 从页面接收事件 的顺序。
事件发生时元素节点之间按照特定的顺序传播,这个过程即DOM事件流。
分为三个阶段:1、事件捕获阶段;2、当前目标阶段;3、事件冒泡阶段
事件捕获阶段(window→触发元素)
首先开始事件捕获阶段,从DOM树最根部的节点window开始,沿着DOM树向下遍历每个元素,直到触发元素目标元素target。如果这些元素也注册了click事件(且为捕获阶段),就会执行他们相应的事件监听函数
事件冒泡阶段(触发元素→window)
最后是事件冒泡阶段,从触发元素目标元素target开始,向上逆着遍历DOM树,直到最根部window元素。如果这些元素也注册了click事件(且为冒泡阶段),就会执行他们相应的事件监听函数
例子
我们为tr元素绑定了一个单击click事件。当我们单击tr时
若单击的是纯tr元素,即触发元素是纯tr元素
- 绑定元素和触发元素相同
- 事件捕获阶段:window → document → html → body → table → tbody → tr
- 事件冒泡阶段:tr → tbody → table → body → html → document → window
- 路径(触发元素到window,也就是冒泡阶段):tr → tbody → table → body → html → document → window
若单击的是tr元素里的td元素(也是tr元素!!!!),即触发元素是孩子
- 绑定元素是触发元素的爸爸
- 事件捕获阶段:window → document → html → body → table → tbody → tr → td
- 事件冒泡阶段:td → tr → tbody → table → body → html → document → window
- 路径(触发元素到window,也就是冒泡阶段):tr → tbody → table → body → html → document → window
如何给元素绑定事件
方法一:在html的那个元素里绑定(不要用!)
<div onclick = console.log('hi')>点我</div>
方法二:给DOM元素的onclick属性赋值事件监听函数
$div.onclick = function(e){
console.log('你刚才单击了这个div元素')
}
缺点:一个元素的一种事件(如click)只能有一个事件监听函数,因为你是给这个元素的这个属性(onclick)赋值啊!下一个值会覆盖上一个值的呀。就像我如果要再给$div绑定一个click事件,再写了一个事件监听函数。这个事件监听函数还是会成为属性onclick的新值的呀
方法三:调用DOM元素的addEventListener函数
优点:可以添加多个事件处理函数。
$div.adddEventListener(type,listener,useCapture)
- type : 事件类型
- listener : 事件监听函数
- useCapture:可选,true表示在捕获阶段调用listener,false(默认)表示在冒泡阶段调用listener。
$div.adddEventListener(type,listener,options)
- options:
- 可选,是个对象。
{capture: 是否捕获阶段监听,once: 是否只监听一次,passive:是否忽略preventDefault }- 默认全是false
移除事件处理程序target.removeEventListener(type,handler,)
通过addEventListener()添加的事件处理程序只能使用removeEventListener() 来移除;移除时传入的参数和添加程序处理程序时使用的参数相同。这也意味通过 addEventListener() 添加的匿名函数(没有传入参数)将无法移除
target.removeEventL .istener('click', handler)
target.removeEventL .istener('click', handler, true)
例子
事件对象
定义理解
var div = document.getElementById('box');
div.onclick = function(event){ };
-
event在这里面就是事件对象,写到事件监听函数函数的小括号() 里面,可以当做形参来看。可以自己随便命名(比如 event、e、aa)。后面用不到事件对象的话,就不需要在小括号里写。
-
事件对象只有有了事件才会存在,事件对象是系统自动给我们创建的,不需要我们传递参数(事件对象必定和事件相关,如果没有事件它就不存在,有了事件就存在了)
-
事件对象是 跟事件相关的一系列相关数据的集合 ,比如说 鼠标点击事件里面就包含了鼠标相关的信息,如鼠标坐标;如果是键盘事件里面就包含了键盘事件的信息,比如判断用户按下了哪个键。
-
我来打印出个事件对象给你们看看,里面全是这次事件的相关信息,也就是这次事件(对象)的属性。
常见的事件对象的属性
e.target:返回触发事件的元素(不一定是绑定事件的元素,如事件委托)e.type:返回事件的类型(比如click、mouseover不带on)e.preventDefault():该方法阻止默认事件(默认行为), 比如不让链接跳转e.stopPropagation():停止事件传播。在这个事件后就没了,捕获或者冒泡阶段就到此为止了。e.path:冒泡路径,从触发元素→window
注意:e.target 和 this 的区别
1、e.target 返回的是触发事件的元素;
2、this 返回的是绑定事件的元素;
区别:e.target 点击了哪个元素就返回那个元素;
this 绑定了哪个元素就返回哪个元素。那么就返回谁。
有的时候,事件的绑定元素和触发元素不是同一个。
-
给
div绑定了单击事件,那么this指向的就是div;e.target指向我们点击的那个对象(那个元素触发的对象),点击的是div,所以e.target指向的就是div----绑定元素和触发元素相同 -
给
ul绑定了单击事件,那么this指向的就是ul;e.target指向我们点击的那个对象(那个元素触发的对象),点击的是红框右上角,就是ul,所以e.target指向的就是ul---绑定元素和触发元素相同 -
给
ul绑定了单击事件,那么this指向的就是ul;e.target指向我们点击的那个对象(那个元素触发的对象),点击的是li,所以e.target指向的就是li---绑定元素和触发元素不同
事件委托
- 事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素;
- 一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
- 事件绑定代理给父元素,由父元素根据事件来源统一处 理
- 适用于可能会新增子元素对场景
- 事件代理实际上是事件冒泡的应用
- 如果在需要有多个DOM事件需要监听的情况下(比如几百条微博点击事件注册),给每一个DOM都绑定监听函数,对性能会有极大的影响,因此,有一解决方案为事件委托。
- 事件委托利用了事件冒泡与event.target
委托的优点
- 减少内存消耗
试想一下,若果我们有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件;
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能;
因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素;
所以事件委托可以减少大量的内存消耗,节约效率。
- 动态绑定事件
比如上述的例子中列表项就几个,我们给每个列表项都绑定了事件; 在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;
所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
jQuery 中的事件委托
$.on: 基本用法:$('.parent').on('click', 'a', function () { console.log('click event on tag a'); }),它是.parent元素之下的a元素的事件代理到$('.parent')之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应事件;$.delegate: 基本用法:$('.parent').delegate('a', 'click', function () { console.log('click event on tag a'); }),同上,并且还有相对应的$.delegate来删除代理的事件;
零碎知识点
- input元素有input事件和change事件
- oninput事件,在输入用户输入时触发,它是在元素值发生变化时立即触发;该事件在
<input>或<textarea>元素的值发生改变时触发。 - onchange 在元素失去焦点时触发