DOM中事件处理概览与原理的全面解析


Posted in Javascript onAugust 16, 2016

事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信,DOM支持大量的事件; 

本文通过这几点向大家详细解析事件处理的基本原理:事件类型、事件目标、事件处理程序、事件对象、事件传播

最后再向大家介绍Event对象; 

一、事件类型(event type):是一个用来说明发生了什么类型事件的全小写的字符串,如‘mouseover'
传统事件类型:表单事件,Window事件,鼠标事件,键盘事件,DOM事件, HTML5事件,触摸屏和移动设备事件等 

二、事件目标(event target):触发事件的对象 

三、事件处理程序(或事件监听程序)(event listener):处理或响应事件的函数。当某对象触发某事件时,浏览器将自动调用在该对象上注册的函数;
注册事件处理程序(监听事件):
1.作为HTML属性注册(只会在冒泡阶段触发)如<table id="t" onclick="modifyText();">;而某些事件类型通常直接在浏览器上触发,而非任何特定文档元素上触发,把这些事件处理程序放在<body>标签上,但浏览器会在Window对象上注册它们,如<body onload="alert('Hello world!')">,这些事件有:
onafterprint onfocus ononline onresize onbeforeprint onhashchange 
onpagehide onstorage onbeforeunload onload onpageshow onundo 
onblur onmessage onpopstate onunload onerror onoffline onredo
作为HTML属性的事件的值是JS代码字符串,是处理函数的主体,不含{},注意:尽量不要在任何其他HTML标签上注册事件,它违反了HTML与JavaScript代码相分离的原则,倘若事件函数可能还没加载进来就点击了事件对象元素,这会导致错误;

2.作为DOM元素的属性来注册(只会在冒泡阶段触发),此时的事件处理程序属性的名字需加‘on'前缀,这种方式兼容所有浏览器,唯一的缺点是只能注册一个事件处理函数,如果定义两次onclick属性,后一次定义会覆盖前一次;如:window.onload = function(){...};

3.除了IE8及之前版本外的所有浏览器中,DOM的事件操作(监听和触发),都定义在EventTarget接口。Element节点、document节点和window对象,都部署了这个接口。此外,XMLHttpRequest、AudioNode、AudioContext等浏览器内置对象,也部署了这个接口。该接口有三个方法,addEventListener和removeEventListener用于绑定和移除监听函数,dispatchEvent用于触发事件;
addEventListener(type,listener,boolean)方法来注册listener,第三个参数设置事件的传播方式,通常使用默认值false,表示监听函数只在冒泡阶段(pupple)被触发,当设为true时,表示监听函数在捕获阶段(capture)触发;可以为同一对象上的同一类型事件注册任意多个listener,所有listener会按照注册顺序触发(注册重复的listener将会被浏览器忽略);
如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数,如elm.addEventListener('click',function(){listen('实参')},false);
当注册的listener是一个对函数的引用变量,就可以用removeEventLestener(type,listener,boolean)在事件目标上删除该listener,对同一监听事件的冒泡事件和捕获事件需要分别删除,两者互不干扰;

var div = document.getElementById('div');
var listener = function (event) {
 
           /* do something here */
        };
 div.addEventListener('click', listener, false);
 
div.removeEventListener('click', listener, false);

dispatchEvent(event)方法在当前节点上手动触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),就返回false,否则为true,参数是一个Event对象的实例,该参数不能为空,且必须是一个有效的事件对象,否则报错;
btn.addEventListener('click', listener, false);
     var e = new Event('click');
btn.dispatchEvent(e); //在btn上立即触发click事件,将立即调用listener 

下面例子根据dispatchEvent方法的返回值,判断事件是否被取消了
var canceled = !btn.dispatchEvent(event);
if (canceled) { console.log('事件取消'); } 
else { console.log('事件未取消'); }} 

4.IE8及之前版本仅支持attachEvent (type,listener)和detachEvent(type,listener),它们的用法和addEventListener的区别:a.参数只有两个;b.参数type必须加'on'前缀;c.它允许对同一监听事件进行重复注册,且都会被调用;d.使用attachEvent方法有个缺点,是this的值会变成 window 对象而不是触发事件的元素;
调用顺序问题:1).通过设置对象属性或HTML属性注册的处理程序一直优先调用;
                         2).使用addEventListener 注册的处理程序按照它们的注册顺序调用;
                         3).旧版IE中使用attachEvent注册的处理程序可能按照任何顺序调用。
返回值问题:

1).事件处理程序的返回值只对通过属性注册的处理程序才有意义,通过设置对象属性或HTML属性注册 事件处理程序的返回值为false,就是告诉浏览器不要执行这个事件相关的默认操作。当浏览器要跳转到新页面时触发Window对象的onbeforeunload事件,若它的的返回值为字符串,则它将出现在询问确认对话框中;

2).addEventListener()或attachEvent()注册事件处理程序若要取消浏览器的默认操作必须调用preventDefault()方法或设置事件对象的returnValue属性。
this指向问题:

1).addEventListener方法指定的监听函数,内部的this对象总是指向触发事件的那个节点;
2).IE8及以前的attachEvent方法注册的事件处理函数的this指向全局对象;
以下写法的this对象都指向Element节点。 
                    element.onclick = print;
                    element.addEventListener('click', print, false)
                    element.onclick = function () {console.log(this.id);}
                    <element onclick="console.log(this.id)">
 以下写法的this对象,都指向全局对象。 
                    element.onclick = function (){ doSomething() }; 
                    element.setAttribute('onclick', 'doSomething()'); 
                    <element onclick="doSomething()"> 
                    element.attachEvent('onclick',doSomething) //IE8
内存问题:对如下代码,每个循环中都会创建一个新的匿名函数,占用的内存越来越多;由于没有保持到匿名函数的引用,它不可能被调用 removeEventListener;所以应当把第二参数listener保持为对处理事件函数的引用;                  

for(i=0 ; i<els.length ; i++){
   els[i].addEventListener("click", function(e){/*do something*/}, false});
 
    }

通用的兼容旧版IE的工具函数:
确保事件处理程序的this指向事件的目标对象的工具函数addEvent

function addEvent(target,type,func){ 
  if(target.addEventListener){ 
    target.addEventListener(type,func,false);
 
  }else{
 
    target.attachEvent('on'+type,function(e){  //这里attachEvent注册的处理函数未绑定引用,所以无法用detachEvent删除
 
      return func.call(target,e);
 
    });
 
  }
 
}

通用的事件处理程序(因为IE8及以前版本,作为事件目标的on-属性的处理程序需要window.event来获得事件对象,且触发事件的目标节点对象通过event.srcElement属性获得) 

function func(event){ 
  var event = event||window.event; 
  var target = event.target || event.srcElement; 
  //......处理程序代码
 
}

四、事件传播(event propagation):是浏览器决定哪个对象触发其事件处理程序的过程。
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段==>处于目标阶段==>事件冒泡阶段。首先发生的是事件捕获阶段(从外层向内层传播),为事件传播经过的所有节点截获事件提供了机会。然后是实际的目标接收事件(按注册顺序执行)。最后一个阶段是冒泡阶段(从内层向外层冒泡)。

当容器元素及嵌套元素,即在捕获阶段又在冒泡阶段调用事件处理程序时:事件按DOM事件流的顺序执行事件处理程序,且当事件处于目标阶段时,事件调用顺序决定于绑定事件的书写顺序

如果希望事件到某个节点为止,不再传播,有两种方式:

1.使用事件对象的event.stopPropagation()方法来阻止当前监听函数的传播;

2.使用事件对象的event.stopImmediatePropagation()方法来阻止当前事件在其事件对象上的所有监听函数的传播; 

事件的代理(delegation):由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件;

五、事件对象(Event):事件发生以后,会生成一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。Event对象本身就是一个构造函数,可以用来生成新的实例。 

var ev = new Event("look", {"bubbles":true, "cancelable":false});
document.dispatchEvent(ev);

Event构造函数接受两个参数。第一个参数是字符串,表示事件的名称;第二个参数是一个对象,表示事件对象的配置。该参数可以有以下两个属性。
bubbles:布尔值,可选,默认为false,表示事件对象是否冒泡。
cancelable:布尔值,可选,默认为false,表示事件是否可以被取消。 

Event对象的属性:
1.与事件的阶段有关: 
bubbles: 只读属性,返回一个布尔值,表示当前事件是否会冒泡,可根据事件是否会冒泡来调用不同的函数。 
eventPhase:返回一个整数值(0,1,2,3之一),表示事件目前所处的状态
<0,事件目前没有发生。
<1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。该过程是从Window对象到Document节点,再到HTMLHtmlElement节点,直到目标节点的父节点为止。
<2,事件到达目标节点,即target属性指向的那个节点。
<3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。该过程是从父节点一直到Window对象。只有bubbles属性为true时,这个阶段才可能发生 

2.与事件的默认行为有关: 
cancelable:返回一个布尔值,表示事件是否可以取消。如果要取消某个事件,需要在这个事件上面调用preventDefault方法 
defaultPrevented:返回一个布尔值,表示该事件是否调用过preventDefault方法。

3.与事件的目标节点有关: 
currentTarget:返回事件执行的监听函数所绑定的那个节点。 
target:返回触发事件的那个节点。在IE6—IE8之中,该属性的名字不是target,而是srcElement

4.与事件对象的其他信息相关: 
type:返回一个字符串,表示事件类型 
detail:返回一个数值,表示事件的某种信息。具体含义与事件类型有关,对于鼠标事件,表示鼠标按键在某个位置按下的次数,比如对于dblclick事件,detail属性的值总是2 
timeStamp:返回一个毫秒时间戳,表示事件发生的时间。从PerformanceTiming.navigationStart开始计算,即表示距离用户导航至该网页的时间。如果想将这个值转为Unix纪元时间戳,就要计算event.timeStamp + performance.timing.navigationStart
isTrusted:返回一个布尔值,表示该事件是否可以信任。用处不大,不同浏览器的支持不一样。

Event对象的方法: 
preventDefault():取消浏览器对当前事件的默认行为,该方法生效的前提是,事件的cancelable属性为true,如果为false,则调用该方法没有任何效果。 
stopPropagation():终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。注意:该方法不能阻止同一个 Document 节点上的其他事件句柄被调用,但是它可以阻止把事件分派到其他节点 
stopImmediatePropagation():阻止同一个事件的其他监听函数被调用,只要其中有一个监听函数调用了该方法,其他的监听函数就不会再执行了。

参考链接: 
http://javascript.ruanyifeng.com/dom/event.html#toc31 
https://developer.mozilla.org/zh-CN/docs/Web/API 
JavaScript权威指南第六版

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js Function类型
Dec 04 Javascript
JavaScript(js)设置默认输入焦点(focus)
Dec 28 Javascript
js匿名函数的调用示例(形式多种多样)
Aug 20 Javascript
JS小游戏之宇宙战机源码详解
Sep 25 Javascript
JavaScript 计算笛卡尔积实例详解
Dec 02 Javascript
javascript数据类型详解
Feb 07 Javascript
Vue数组更新及过滤排序功能
Aug 10 Javascript
ReactNative实现Toast的示例
Dec 31 Javascript
jQuery实现form表单序列化转换为json对象功能示例
May 23 jQuery
详解小程序输入框闪烁及重影BUG解决方案
Aug 31 Javascript
react-native滑动吸顶效果的实现过程
Jun 03 Javascript
vue父子模板传值问题解决方法案例分析
Feb 26 Javascript
总结javascript中的六种迭代器
Aug 16 #Javascript
你知道setTimeout是如何运行的吗?
Aug 16 #Javascript
深入理解(function(){... })();
Aug 16 #Javascript
关于JSON与JSONP简单总结
Aug 16 #Javascript
json与jsonp知识小结(推荐)
Aug 16 #Javascript
浅谈JS继承_借用构造函数 &amp; 组合式继承
Aug 16 #Javascript
JS读写CSS样式的方法汇总
Aug 16 #Javascript
You might like
php 无法加载mcrypt.dll的解决办法
2013/04/03 PHP
php结合md5实现的加密解密方法
2016/01/25 PHP
转换json格式的日期为Javascript对象的函数
2010/07/13 Javascript
javascript实现div的拖动并调整大小类似qq空间个性编辑模块
2012/12/12 Javascript
js单向链表的具体实现实例
2013/06/21 Javascript
JS与C#编码解码
2013/12/03 Javascript
JavaScript中使用arguments获得函数传参个数实例
2014/08/27 Javascript
JavaScript实现在标题栏上显示当前日期的方法
2015/03/19 Javascript
javascript仿京东导航左侧分类导航下拉菜单效果
2020/11/25 Javascript
jQuery中delegate()方法的用法详解
2016/10/13 Javascript
jQuery的事件预绑定
2016/12/05 Javascript
js Date()日期函数浏览器兼容问题解决方法
2017/09/12 Javascript
node基于puppeteer模拟登录抓取页面的实现
2018/05/09 Javascript
Vuex实现数据增加和删除功能
2019/11/11 Javascript
微信小程序吸底区域适配iPhoneX的实现
2020/04/09 Javascript
微信小程序用户盒子、宫格列表的实现
2020/07/01 Javascript
Python绘制的二项分布概率图示例
2018/08/22 Python
深入理解Python中的 __new__ 和 __init__及区别介绍
2018/09/17 Python
解决Pycharm 导入其他文件夹源码的2种方法
2020/02/12 Python
Python tempfile模块生成临时文件和临时目录
2020/09/30 Python
Rowdy Gentleman服装和配饰:美好时光
2019/09/24 全球购物
如何进行有效的自我评价
2013/09/27 职场文书
大学生毕业自我鉴定
2013/11/06 职场文书
专科文秘应届生求职信
2013/11/18 职场文书
工作失职检讨书范文
2014/01/16 职场文书
安全生产检查通报
2014/01/29 职场文书
学生会离职感言
2014/02/11 职场文书
12月红领巾广播稿
2014/02/13 职场文书
超市国庆节促销方案
2014/02/20 职场文书
能源工程专业应届生求职信
2014/03/01 职场文书
中学生学雷锋演讲稿
2014/04/26 职场文书
推荐信格式要求
2014/05/09 职场文书
幼儿园教师自荐书
2015/03/06 职场文书
盗窃罪辩护词范文
2015/05/21 职场文书
新闻简讯格式及范文
2015/07/22 职场文书
浅谈哪个Python库才最适合做数据可视化
2021/06/28 Python