DOM事件机制
一、dom事件级别
dom级别一共可以分为四个级别:dom0级、dom1级、dom2级和dom3级。而dom事件分为3个级别:dom 0级事件处理,dom 2级事件处理和dom 3级事件处理。由于dom 1级中没有事件的相关内容,所以没有dom 1级事件。
1.dom 0级事件
el.onclick=function(){}
// 例1
var btn = document.getelementbyid('btn');
btn.onclick = function(){
alert(this.innerhtml);
}
当希望为同一个元素/标签绑定多个同类型事件的时候(如给上面的这个btn元素绑定3个点击事件),是不被允许的。dom0事件绑定,给元素的事件行为绑定方法,这些方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的。
2.dom 2级事件
el.addeventlistener(event-name, callback, usecapture)
event-name: 事件名称,可以是标准的dom事件 callback: 回调函数,当事件触发时,函数会被注入一个参数为当前的事件对象 event usecapture: 默认是false,代表事件句柄在冒泡阶段执行
// 例2
var btn = document.getelementbyid('btn');
btn.addeventlistener("click", test, false);
function test(e){
e = e || window.event;
alert((e.target || e.srcelement).innerhtml);
btn.removeeventlistener("click", test)
}
//ie9-:attachevent()与detachevent()。
//ie9+/chrom/ff:addeventlistener()和removeeventlistener()
ie9以下的ie不支持 addeventlistener()和removeeventlistener(),使用 attachevent()与detachevent() 代替,因为ie9以下是不支持事件捕获的,所以也没有第三个参数,第一个事件名称前要加on。
3.dom 3级事件
在dom 2级事件的基础上添加了更多的事件类型。
ui事件,当用户与页面上的元素交互时触发,如:load、scroll 焦点事件,当元素获得或失去焦点时触发,如:blur、focus 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dblclick、mouseup 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel 文本事件,当在文档中输入文本时触发,如:textinput 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress 合成事件,当为ime(输入法编辑器)输入字符时触发,如:compositionstart 变动事件,当底层dom结构发生变化时触发,如:domsubtreemodified 同时dom3级事件也允许使用者自定义一些事件。
二、dom事件模型和事件流
dom事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
dom事件捕获的具体流程
捕获是从上到下,事件先从window对象,然后再到document(对象),然后是html标签(通过document.documentelement获取html标签),然后是body标签(通过document.body获取body标签),然后按照普通的html结构一层一层往下传,最后到达目标元素。
而事件冒泡的流程刚好是事件捕获的逆过程。
接下来我们看个事件冒泡的例子:
// 例3
...... window.onclick = function() { console.log('window'); }; document.onclick = function() { console.log('document'); }; document.documentelement.onclick = function() { console.log('html'); }; document.body.onclick = function() { console.log('body'); } outer.onclick = function(ev) { console.log('outer'); }; inner.onclick = function(ev) { console.log('inner'); };
正如我们上面提到的onclick给元素的事件行为绑定方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的。
三、事件代理(事件委托)
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)。
1.优点
减少内存消耗,提高性能
假设有一个列表,列表之中有大量的列表项,我们需要在点击每个列表项的时候响应一个事件
// 例4
item 1
item 2
item 3
......
item n
如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。借助事件代理,我们只需要给父容器ul绑定方法即可,这样不管点击的是哪一个后代元素,都会根据冒泡传播的传递机制,把容器的click行为触发,然后把对应的方法执行,根据事件源,我们可以知道点击的是谁,从而完成不同的事。
动态绑定事件
在很多时候,我们需要通过用户操作动态的增删列表项元素,如果一开始给每个子元素绑定事件,那么在列表发生变化时,就需要重新给新增的元素绑定事件,给即将删去的元素解绑事件,如果用事件代理就会省去很多这样麻烦。
2.如何实现
接下来我们来实现上例中父层元素 #list 下的 li 元素的事件委托到它的父层元素上:
// 给父层元素绑定事件
document.getelementbyid('list').addeventlistener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcelement;
// 判断是否匹配目标元素
if (target.nodename.tolocalelowercase === 'li') {
console.log('the content is: ', target.innerhtml);
}
});
四、event对象常见的应用
event. preventdefault()
如果调用这个方法,默认事件行为将不再触发。什么是默认事件呢?例如表单一点击提交按钮(submit)跳转页面、a标签默认页面跳转或是瞄点定位等。
很多时候我们使用a标签仅仅是想当做一个普通的按钮,点击实现一个功能,不想页面跳转,也不想瞄点定位。
//方法一:
链接
也可以通过js方法来阻止,给其click事件绑定方法,当我们点击a标签的时候,先触发click事件,其次才会执行自己的默认行为
//方法二:
链接
//方法三:
链接
接下来我们看个例子:输入框最多只能输入六个字符,如何实现?
// 例5
event.stoppropagation() & event.stopimmediatepropagation()
event.stoppropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。上面提到事件冒泡阶段是指事件从目标节点自下而上向window对象传播的阶段。
我们在例4的inner元素click事件上,添加event.stoppropagation()这句话后,就阻止了父事件的执行,最后只打印了'inner'。
inner.onclick = function(ev) {
console.log('inner');
ev.stoppropagation();
};
stopimmediatepropagation 既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stoppropagation 只能实现前者的效果。我们来看个例子:
click me to stop propagation...... const btn = document.queryselector('#btn'); btn.addeventlistener('click', event => { console.log('btn click 1'); event.stopimmediatepropagation(); }); btn.addeventlistener('click', event => { console.log('btn click 2'); }); document.body.addeventlistener('click', () => { console.log('body click'); }); // btn click 1
如上所示,使用 stopimmediatepropagation后,点击按钮时,不仅body绑定事件不会触发,与此同时按钮的另一个点击事件也不触发。
event.target & event.currenttarget
老实说这两者的区别,并不好用文字描述,我们先来看个例子:
当我们点击最里层的元素d的时候,会依次输出:
target:d¤ttarget:d
target:d¤ttarget:c
target:d¤ttarget:b
target:d¤ttarget:a
从输出中我们可以看到,event.target指向引起触发事件的元素,而event.currenttarget则是事件绑定的元素,只有被点击的那个目标元素的event.target才会等于event.currenttarget。也就是说,event.currenttarget始终是监听事件者,而event.target是事件的真正发出者。
参考文章
dom级别与dom事件
dom事件机制解惑
事件模型
javascript 事件委托详解
javascript 事件的学与记:stoppropagation 和 stopimmediatepropagation
event.target和event.currenttarget的区别
推荐阅读
-
关于微信公众平台用户发送关键词的回复机制
-
java多线程等待唤醒机制、 Lambda表达式、单例设计模式
-
用PHP实现动态产生XML文件以及从XML文件中抽取数据转化成HTML的小程序。。。不用DOM的。。_PHP教程
-
PHP5中使用DOM控制XML实现代码
-
Java反射机制 博客分类: J2SE java设计模式spring反射DI
-
iOS屏幕截图事件
-
不要使用jQuery触发原生事件的方法
-
Spring架构揭秘-事件监听机制 博客分类: Spring springjava事件监听IOC容器事件
-
YUI学习笔记(二)简单的DOM事件(2) 博客分类: YUI YUI
-
关于页面刷新,事件重复提交的方法分享