为什么要使用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(){ ...
随机推荐
- TensorFlow 安装详解
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! 『不要把手段当成目标 — <一个瑜伽行者的自传>』 本文提纲 1. 机器学习 2 ...
- Xamarin+Prism开发之.net standard化
前面[Visual Studio 2017创建.net standard类库编译出错原因]已经解决.net standard类库的编译问题,现在可以正式进入主题了.作为.net 跨平台开发者也得跟上时 ...
- laravel blade $loop
laravel 5.3 blade 新增$loop变量 文档如下: 在Laravel 5.3中,@foreach指令提供了更加强大的功能,在每一个@foreach循环体中都可以调用一个新的$loop变 ...
- Android 学习笔记之 个人认为最简单的查看Android源码方案
相信很多人都会疑惑如何使用Eclipse ADT查看源码? 下面我们将介绍 如何查看Android源码. 本文有如下优点: 1.不用费心去找Android源码地址:一个字烦,网上的东西杂七杂八的... ...
- 【CSS/JS学习】如何实现单行/多行文本溢出的省略(...)--老司机绕过坑道的正确姿势
引言: 写前端UI的朋友们也许都遇到过这样的问题:我们需要实现这样一个需求,在一个父级元素中隐藏一个可能过长的文本: 这个文本可能是单行的: 也可能是多行的: 下面我就给大家展示如何简单或 ...
- 锋利的jQuery(1)——DOM对象与jQuery对象的转换
1.认识jQuery DOM对象:即文档对象模型 jQuery对象:通过jQuery包装DOM对象后产生的对象.这是jQuery对象所特有的,在jQuery对象中无法使用DOM对象的任何方法,同时DO ...
- VS2017 Cordova Ionic2 移动开发-环境搭建
1. 文档概述 本文档用于说明Visual Studio 2017下使用 Ionic 2进行跨平台开发的运行环境配置. 2. 安装环境 Windows10 3. 安装 Visual Studio 20 ...
- 解决Json传输中文乱码问题
1.如果是通过URL传递:----需要编码两次 var searchText = this.searchText(); searchText = encodeURI(searchText); sear ...
- Java 原始数据类型
如何记住 Java 中的原始数据类型? 画了一个图方便记忆:
- Java 原始数据类型的计算:运算符重载(Operator Overload)和类型转换(Type Conversion)
原文阅读:<算法(第四版)>第一章 第一节:基础编程模型 有没有在面试的时候被问到:下面这几行代码的执行结果是什么?依据是什么? System.out.println (5/3); Sys ...