DOM中事件处理概览与原理的全面解析
事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信,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
狼蚁网站SEO优化例子根据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*