为什么要使用addEventListener而不是on监听事件
昨天回答了一个关于vue的问题 vue 除了input 其他可以用keyup事件嘛? 在vue中没有提供除表单之外其它的keyup绑定方法,可以使用原生的监控键盘的事件,于是给出了代码:
mounted() {
document.onkeydown=function(e) {
if(e && e.keyCode==83 && e.altKey ){ //同时按下Alt+s
//要做的事情
}
}
}
这段代码本身没有什么错误,但是一位知友评论为什么不用addEventListener呢?addEventListener也确实可以,那么addEventListener和on之间有什么区别呢?
查阅addEventListener文档发现他们还确实有点区别
为什么要使用 addEventListener?
addEventListener 是 W3C DOM 规范中提供的注册事件监听器的方法。它的优点包括:
- 它允许给一个事件注册多个
listener。当存在其他的库时,使用 DHTML 库或者 Mozilla extensions 不会出现问题。 - 它提供了一种更精细的手段控制
listener的触发阶段。(即可以选择捕获或者冒泡)。 - 它对任何 DOM 元素都是有效的,而不仅仅只对 HTML 元素有效。
在事件分派时添加事件处理器
当一个 EventListener 在 EventTarget 正在处理事件的时候被注册到 EventTarget 上,它不会被立即触发,但可能在事件流后面的事件触发阶段被触发,例如可能在捕获阶段添加,然后在冒泡阶段被触发。
多个相同的事件处理器
同一个 EventTarget 注册了多个相同的 EventListener,那么重复的实例会被抛弃。所以这么做不会使得 EventListener 被调用两次,也不需要用 removeEventListener 手动清除多余的EventListener ,因为重复的都被自动抛弃了。
处理过程中 this 的值的问题
通常来说this的值是触发事件的元素的引用,这种特性在多个相似的元素使用同一个通用事件监听器时非常让人满意。
当使用 addEventListener() 为一个元素注册事件的时候,句柄里的 this 值是该元素的引用。其与传递给句柄的 event 参数的 currentTarget 属性的值一样。比如下面的例子:
<table id="t" onclick="modifyText();">
...
这时modifyText()中的this 的值会变成全局 (window) 对象的引用(在严格模式中为 undefined);
注意: JavaScript 1.8.5 引入了Function.prototype.bind() 方法,允许制定函数调用时的 this 的值。这使得想要绕开由于调用情况不同,this 取值不同的问题变得十分容易 。然而请注意,你应该保留一个 listener 的引用,以便在未来需要的时候能够比较好地移除。
下面是 bind 相关的例子:
var Something = function(element) {
// |this| is a newly created object
this.name = 'Something Good';
this.onclick1 = function(event) {
console.log(this.name); // undefined, as |this| is the element
};
this.onclick2 = function(event) {
console.log(this.name); // 'Something Good', as |this| is bound to newly created object
};
element.addEventListener('click', this.onclick1, false);
element.addEventListener('click', this.onclick2.bind(this), false); // Trick
}
var s = new Something(document.body);
上面这个例子的一个问题是不可能移除使用了 bind 的 listener。一种解决办法是使用定制的函数去捕获任意类型:
var Something = function(element) {
// |this| is a newly created object
this.name = 'Something Good';
this.handleEvent = function(event) {
console.log(this.name); // 'Something Good', as this is bound to newly created object
switch(event.type) {
case 'click':
// some code here...
break;
case 'dblclick':
// some code here...
break;
}
};
// Note that the listeners in this case are |this|, not this.handleEvent
element.addEventListener('click', this, false);
element.addEventListener('dblclick', this, false);
// You can properly remove the listeners
element.removeEventListener('click', this, false);
element.removeEventListener('dblclick', this, false);
}
var s = new Something(document.body);
传统的 Internet Explorer 及其 attachEvent 方法
对于 Internet Explorer 来说,在IE 9之前,你必须使用 attachEvent 而不是使用标准方法 addEventListener。为了支持IE,前面的例子需要改成这样:
if (el.addEventListener) {
el.addEventListener('click', modifyText, false);
} else if (el.attachEvent) {
el.attachEvent('onclick', modifyText);
}
使用 attachEvent 方法有个缺点,this 的值会变成 window 对象的引用而不是触发事件的元素。
兼容性
Note: IE8 不具有任何替代 useCapture 的方法,useCapture 是 IE8 不支持的。 请注意下面的代码只能添加 IE8。另外请注意,下面这个 IE8 polyfill 只适用于标准模式:需要 DOCTYPE 声明。
(function() {
if (!Event.prototype.preventDefault) {
Event.prototype.preventDefault=function() {
this.returnValue=false;
};
}
if (!Event.prototype.stopPropagation) {
Event.prototype.stopPropagation=function() {
this.cancelBubble=true;
};
}
if (!Element.prototype.addEventListener) {
var eventListeners=[];
var addEventListener=function(type,listener /*, useCapture (will be ignored) */) {
var self=this;
var wrapper=function(e) {
e.target=e.srcElement;
e.currentTarget=self;
if (typeof listener.handleEvent != 'undefined') {
listener.handleEvent(e);
} else {
listener.call(self,e);
}
};
if (type=="DOMContentLoaded") {
var wrapper2=function(e) {
if (document.readyState=="complete") {
wrapper(e);
}
};
document.attachEvent("onreadystatechange",wrapper2);
eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2});
if (document.readyState=="complete") {
var e=new Event();
e.srcElement=window;
wrapper2(e);
}
} else {
this.attachEvent("on"+type,wrapper);
eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper});
}
};
var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) {
var counter=0;
while (counter<eventListeners.length) {
var eventListener=eventListeners[counter];
if (eventListener.object==this && eventListener.type==type && eventListener.listener==listener) {
if (type=="DOMContentLoaded") {
this.detachEvent("onreadystatechange",eventListener.wrapper);
} else {
this.detachEvent("on"+type,eventListener.wrapper);
}
eventListeners.splice(counter, 1);
break;
}
++counter;
}
};
Element.prototype.addEventListener=addEventListener;
Element.prototype.removeEventListener=removeEventListener;
if (HTMLDocument) {
HTMLDocument.prototype.addEventListener=addEventListener;
HTMLDocument.prototype.removeEventListener=removeEventListener;
}
if (Window) {
Window.prototype.addEventListener=addEventListener;
Window.prototype.removeEventListener=removeEventListener;
}
}
})();
注册 listener 的旧方法
addEventListener() 在DOM 2 Events 规范中引入。在这之前,事件监听器应该用以下的方法注册:
// Pass a function reference — do not add '()' after it, which would call the function!
el.onclick = modifyText; // Using a function expression
element.onclick = function() {
// ... function logic ...
};
这个方法会替换这个元素上所有已存在的 onclick 事件。对于其他事件是类似的,比如 blur (onblur)、 keypress (onkeypress)等等。
由于这是 DOM 0 规范的基本内容,几乎所有浏览器都支持这个,而且不需要特殊的跨浏览器兼容代码。因此通常这个方法被用于动态地注册时间处理器,除非必须使用 addEventListener() 才能提供的特殊特性。
内存问题
var i;
var els = document.getElementsByTagName('*'); // Case 1
for(i=0 ; i<els.length ; i++){
els[i].addEventListener("click", function(e){/*do something*/}, false});
} // Case 2
function processEvent(e){
/*do something*/
} for(i=0 ; i<els.length ; i++){
els[i].addEventListener("click", processEvent, false});
}
在第一种情况下,每个循环中都会创建一个新的(匿名)函数。在第二种情况下,会使用先前声明的相同的函数作为事件处理器。这样的结果是占用的存储空间更小。而且,在第一种情况中,由于没有保持到匿名函数的引用,它不可能被调用 element.removeEventListener,这是因为我们没有一个可参考的处理器,而在第二种情况,它可以被 myElement.removeEventListener("click", processEvent, false)。
使用 passive 改善的滚屏性能
var elem = document.getElementById('elem');
elem.addEventListener('touchmove', function listener() { /* do something */ }, { passive: true });
example:
<div class="box">ooxx</div>
window.onload = function(){
var box = document.getElementById("box");
box.onclick = function(){
console.log("我是box1");
}
box.onclick = function(){
box.style.fontSize = "18px";
console.log("我是box2");
}
}
运行结果:“我是box2”
第二个onclick把第一个onclick给覆盖了,虽然大部分情况我们用on就可以完成我们想要的结果,但是有时我们又需要执行多个相同的事件,很明显如果用on完成不了我们想要的,那不用猜,你们肯定知道了,对!addEventListener可以多次绑定同一个事件并且不会覆盖上一个事件。
用addEventListener的代码:
window.onload = function(){
var box = document.getElementById("box");
box.addEventListener("click",function(){
console.log("我是box1");
})
box.addEventListener("click",function(){
console.log("我是box2");
})
}
运行结果:我是box1
我是box2
addEventListenert方法第一个参数填写事件名,注意不需要写on,第二个参数可以是一个函数,第三个参数是指在冒泡阶段还是捕获阶段处理事件处理程序,如果为true代表捕获阶段处理,如果是false代表冒泡阶段处理,第三个参数可以省略,大多数情况也不需要用到第三个参数,不写第三个参数默认false
第三个参数的使用:
有时候的情况是这样的
<body>
<div id="box">
<div id="child"></div>
</div>
</body>
如果我给box加click事件,如果我直接单击box没有什么问题,但是如果我单击的是child元素,那么它是怎么样执行的?(执行顺序)
box.addEventListener("click",function(){
console.log("box");
})
child.addEventListener("click",function(){
console.log("child");
})
执行的结果:
child
box
也就是说,默认情况事件是按照事件冒泡的执行顺序进行的。
如果第三个参数写的是true,则按照事件捕获的执行顺序进行的。
box.addEventListener("click",function(){
console.log("box");
},true)
child.addEventListener("click",function(){
console.log("child");
})
执行的结果:
box
child
事件冒泡执行过程:
从最具体的的元素(你单击的那个元素)开始向上开始冒泡,拿我们上面的案例讲它的顺序是:child->box
事件捕获执行过程:
从最不具体的元素(最外面的那个盒子)开始向里面冒泡,拿我们上面的案例讲它的顺序是:box->child
为什么要使用addEventListener而不是on监听事件的更多相关文章
- vue 中监听窗口发生变化,触发监听事件, window.onresize && window.addEventListener('resize',fn) ,window.onresize无效的处理方式
// 开始这样写,不执行 window.onresize = function() { console.log('窗口发生变化') } // 改成window监听事件 window.addEventL ...
- DOM 事件监听 事件冒泡 事件捕获
addEventListener() 方法 实例: // 当用户点击按钮时触发监听事件: document.getElementById("myBtn").addEventList ...
- javascript事件有哪些?javascript的监听事件
事件类型: 1.界面事件 onload:描述文档,图片,css已经frame,object加载完毕时触发,window.onload window.onload = function(){ //代表图 ...
- 浅谈postMessage多页面监听事件
最近做了一个Echarts和Highcharts多图多页面连动的效果,就用到postMessage 如下介绍: 最开始在最外围的页面也就是所有页面的父级页面添加postMessage监听事件以便监听下 ...
- js监听事件 上滑消失下滑出现的效果 触摸与手势事件
https://www.w3cmm.com/javascript/touch.html //触摸与手势事件连接tinyscrollbar //方法1var _this = $('#fabu');var ...
- JAVAscript学习笔记 js句柄监听事件 第四节 (原创) 参考js使用表
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- js监听事件
//// 监听事件 监听使用各种浏览器 // var p1 = document.getElementById("p1"); //// 监听事件 // eventUtil.addE ...
- 横向滑动的listview和其中用到的触摸监听事件详解
一.首先把横向的listview的代码放上来 HorizontalListView: package com.common.cklibrary.utils.myview; import java.ut ...
- js 监听事件的叠加和移除
html DOM元素有很多on开头的监听事件,如onload.onclick等,见DOM事件列表.但是同一种事件,后面注册的会覆盖前面的: window.onresize = function(){ ...
随机推荐
- 回答: 2017-03-19的关于css+div布局的疑问 2017-04-05
原问题为红色,回答为黑色 第一次布局一个静态网页,发现许多细节都需要自己探索,出现诸如以下问题: 1.布局问题:经常出现一个div被挤出来?做到一半少一个div布局? 布局之前,要点打好框架,明确每个 ...
- LoonAndroid自动检测输入框 --- Author: rose && lvyerose@163.com
LoonAndroid框架,同时给我们提供了一套自动检测输入规则的工具,用起来很是方便,下面介绍一下这个东东的使用方法(注意,该说明是基于项目已经集成了LoonAndroid框架而言,如果您未集成该框 ...
- ARM中断处理过程
以s3c2440 ARM9核为例: 一:s3c2440 ARM处理器特性: 1.S3C2440支持个中断源,含子中断源: 2.ARM9采用五级流水线方式: 3.支持外部中断和内部中断: 二.s3c2 ...
- 跟着刚哥梳理java知识点——集合(十二)
Java集合分为Collection和Map两种体系 一.Collection接口: Collections接口为我们提供了以下方法: size():返回集合中元素的个数 add(Object obj ...
- 关于constraint的用法
1.主键约束:要对一个列加主键约束的话,这列就必须要满足的条件就是非空因为主键约束:就是对一个列进行了约束,约束为(非空.不重复)以下是代码 要对一个列加主键,列名为id,表名为emp格式为:alt ...
- 通过virtualbox最小化安装centos 6.3后无法上网解决办法
通过virtualbox最小化安装centos 6.3后无法上网解决办法 1.设置virtualbox的网络连接方式,如下图使用桥接方式,桥接的网卡为宿主正在上网的网卡,现在我是通过无线来上网的,所以 ...
- vim工具
今天和同事讨论使用什么看代码,使用什么写代码的问题.我觉得source insight用来看代码真的还是蛮舒服的,但是他觉得他习惯了VS,用着顺手. 但是我的想法是,有好的工具,得先花点时间去学习使用 ...
- 卷积神经网络CNN与深度学习常用框架的介绍与使用
一.神经网络为什么比传统的分类器好 1.传统的分类器有 LR(逻辑斯特回归) 或者 linear SVM ,多用来做线性分割,假如所有的样本可以看做一个个点,如下图,有蓝色的点和绿色的点,传统的分类器 ...
- js 数组方法总结
Array数组: length属性 可通过array.length增加或者减少数组的长度,如;array.length=4(数组长3,第四位为undefined),也可单纯获得长度.array[arr ...
- python13_day4
上周复习 1,python基础 2,基本数据类型 3,函数式编程 函数式编程.三元运行.内置函数.文件处理 容易出问题的点 函数默认返回值为none,对于列表字典,传入引用. 1 2 3 4 5 6 ...