在JavaScript中对于事件来讲,首先,我们需要了解这样几个概念:事件;事件处理程序;事件类型;事件流;事件冒泡;事件捕获;事件对象;浏览器内核;事件绑定;事件方面的性能优化(事件委托、移除事件处理程序);常见的浏览器兼容问题等。
什么是事件event:
JavaScript事件是:浏览器、文档(document)窗口中的发生的特定的交互瞬间;而JavaScript和HTML之间的交互行为就是通过事件来触发的。
事件处理程序:
事件处理程序:我们用户在页面中进行的点击这个动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,
即:click、mousemove、load等都是事件的名称。响应某个事件的函数则称为事件处理程序,或者叫做事件侦听器。
事件类型:
在JavaScript中事件大至分为了三大类,分别是一般事件、表单事件、页面事件这3种。
UI事件:如load、unload、error、resize、scroll、select、DOMActive,是用户与页面上的元素交互时触发的。
焦点事件:如blur、DOMFocusIn、DOMFocusOut、focus、focusin、focusout,在元素获得或失去焦点的时候触发,这些事件当中,最为重要的是blur和focus,有一点需要引起注意,这一类事件不会发生冒泡!
鼠标与滚轮事件:如click、dblclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup,是当用户通过鼠标在页面执行操作时所触发的。
滚轮事件:mousewheel(IE6+均支持)、DOMMouseScroll(FF支持的,与mousewheel效果一样)。是使用鼠标滚轮时触发的。
文本事件:textInput,在文档中输入文本触发。
键盘事件:keydown、keyup、keypress,当用户通过键盘在页面中执行操作时触发。
合成事件:DOM3级新增,用于处理IME的输入序列。所谓IME,指的是输入法编辑器,可以让用户输入在物理键盘上找不到的字符。compositionstart、compositionupdate、compositionend三种事件。
变动事件:DOMsubtreeModified、DOMNodeInserted、DOMNodeRemoved、DOMAttrModified、DOMCharacterDataModified等,当底层DOM结构发生变化时触发。IE8-不支持。
变动名称事件:指的是当元素或者属性名变动时触发,当前已经弃用!
对于事件的基本类型,随着HTML5的出现和发展,又新增了HTML5事件、设备事件(单点触控)、触摸事件touch、手势事件等各种事件等。
事件流:
浏览器层次顺序:document -> html -> body -> div父元素 -> input子元素】,document最上层祖先元素, input最下层后代元素。
什么是事件流:事件流是描述从页面中接收事件的顺序【从内到外(冒泡),从外到内(捕获)】;
IE与原来的NetScape(网景),对于事件流提出的是完全不同的顺序。IE团队提出的是事件冒泡流;NetScape的事件流是事件捕获流。
简单的讲:当给一个DIV绑定一个点击事件,又在DIV里面放一个按扭并给按扭也绑定一个点击事件,此时你点击里面按扭的同时,外面DIV的点击事件也会被触发。
【事件冒泡】、JS阻止事件冒泡方法:event.stopPropagation(),JS阻止默认行为方法:event.preventDefault() 注:这两个方法属于event的
所谓事件冒泡 就是事件最开始从最具体的元素(文档中嵌套层次最深的那个点【当前绑定事件的那个元素】)接收,然后逐级向上传播至最不具体的那个节点(document节点,即最上层的节点)。
【事件捕获】、和事件冒泡刚好【相反】,它是事件从最不具体的节点(document)先接收到事件,然后再向下逐一捕获至(文档中嵌套层次最深的那个点【当前绑定事件的那个元素】)。
简单地说,事件冒泡和事件捕获都是一种事件传递的机制。这种机制可以使事件在不同级
的元素间传递。事件冒泡是从事件触发的源节点,向父节点传递,直到到达最顶节点。而事件
捕获则是从最顶节点,逐步向下传递,直到到达事件触发的源节点。
在一些标准的浏览器中,如IE9以上,Chrome 、Firefox 、Safari浏览器等,支持这两种冒泡方式。而事实上,
准确的说,是这两种方式的混合方式。因为W3C采取的就是这两种方式的结合:先从顶级节点
开始,将事件向下传递至源节点,再从源节点冒泡至顶节点。
而在IE及Opera(以下说的都是老版本的欧朋,新版本的欧朋经检测是支持事件捕获的)
下,是不支持事件捕获的。而这个特点最明显体现在事件绑定函数上。IE、Opera的事件
绑定函数是attachEvent,而Chrome等浏览器则是addEventListener,
而后者比前者的参数多了一个——这个参数是一个布尔值,这个布尔值由用户决定,用户若设为true,
则该绑定事件以事件捕获的形式参与,若为false则以事件冒泡的形势参与。
而这个参数在IE和Opera浏览器中是不存在的——根本原因是它们不支持事件捕获。
事件对象:
事件对象:在触发DOM上的某个事件的时候,会产生一个事件对象event,而在这个对象当中会包含着所有与事件有关的信息。
事件对象其中有两个信息,我们最为常用,分别是type和target:
type表示的是被触发事件的类型;
target表示的是事件的目标。
其他:
bubbles:表示事件是否冒泡
cancelable:表示是否可以取消事件的默认行为
currentTarget:表示事件处理程序当前正在处理事件的那个元素
defaultPrevented:表示是否调用了preventDefault()
detail:表示的是与事件相关的细节信息
eventPhase:调用事件处理处理程序的阶段:1表示捕获阶段、2表示处于目标、3表示冒泡阶段
浏览器内核:
1、Trident:(IE内核) Trident内核的常见浏览器有:
IE6、IE7、IE8(Trident 4.0)、IE9(Trident 5.0)、IE10(Trident 6.0);
360安全浏览器(1.0-5.0为Trident,6.0为Trident+Webkit,7.0为Trident+Blink)猎豹极轻浏览器,360极速浏览器(7.5之前为Trident+Webkit,7.5为Trident+Blink)
猎豹安全浏览器(1.0-4.2版本为Trident+Webkit,4.3及以后版本为Trident+Blink)猎豹极轻浏览器,
傲游浏览器(傲游1.x、2.x为IE内核,3.x为IE与Webkit双核)、百度浏览器(早期版本)世界之窗浏览器
最初为IE内核,2013年采用Chrome+IE内核)、2345浏览器、腾讯TT、淘宝浏览器、采编读浏览器、搜狗高速浏览器(1.x为Trident,2.0及以后版本为Trident+Webkit)、阿云浏览器(早期版本)、
瑞星安全浏览器、Slim Browser、 GreenBrowser、爱帆浏览器(12 之前版本)、115浏览器、155浏览器、闪游浏览器、N氧化碳浏览器、糖果浏览器、彩虹浏览器、瑞影浏览器、
勇者无疆浏览器、114浏览器、蚂蚁浏览器、飞腾浏览器、速达浏览器、佐罗浏览器、海豚浏览器(iPhone/iPad/Android)、UC浏览器(Blink内核+Trident内核)等。
2、Gecko:(Firefox内核) Netscape6开始采用的内核,后来的Mozilla FireFox(火狐浏览器) 也采用了该内核,Gecko的特点是代码完全公开。
3、Presto:(Opera前内核) (已废弃) Opera现已改用Google Chrome的Blink内核。
4、Webkit:(Safari内核,Chrome内核原型,开源):它是苹果公司自己的内核,也是苹果的Safari浏览器使用的内核 【是现在大部份移动端:Android安桌,apple苹果采用的内核】。
JvaScript事件的三种绑定方式【事件处理程序】:
事件绑定方式一【HTML事件处理程序】:就是将事件直接内嵌内HTML结构标签元素内的 【不推荐用,因为不灵活】
例:
<input type="button" οnclick="alert("我是事件绑定方式一:HTML事件处理程序,我是内嵌在HTML结构中的");" value="事件绑定方式一【内嵌】" />
或 <input type="button" οnclick="mupiaoFn()" value="事件绑定方式一【调用】" />
/*------事件绑定方式一:HTML事件处理程序------*/
function mupiaoFn(){
alert("我是事件绑定方式一:HTML事件处理程序");
}
事件绑定方式二【DOM 0级事件处理程序】:就是把一个函数/方法赋给一个事件处理程的 属性如:id 、class 、元素名等 【用得最多,兼容性好,简单,灵活,跨浏览器 ;缺点:不能绑定多个同类型事件】
例:
<input type="button" name="eventBtn2" id="eventBtn2" value="事件绑定方式二【通用属性绑定】" />
/*------事件绑定方式二:DOM 0级事件处理程序------*/
var Btn2 = document.getElementById("eventBtn2");//给谁绑定事件,就要先获取谁
//绑定事件1:【赋给方式】
Btn2.onclick = function(){
alert("我是事件绑定方式二:DOM 0级事件处理程序");
}
或
//绑定事件2:【调用方式】
function publick(){
alert("我也是事件绑定方式二:DOM 0级事件处理程序");
}
Btn2.onclick = publick; //注:publick后面不要加()括号,否则会变为立即执行函数!
//删除事件:
Btn2.onclick = null; 注:
在DOM0级事件处理程序推出之后,被广为使用,可是出现了这样一个问题,当我们希望给同一个元素/标签绑定多个同类型事件的时候(如,为上面的按扭标签绑定2个或是个以上的点击事件),是不被允许的。
那么,此时,出现了另一种事件处理程序,就是DOM2级的事件处理程序,【注:没有DOM1级事件这个概念哦】在DOM2级当中,定义了两个基本方法,
用于处理指定(即绑定)和删除事件处理程序的操作,分别是addEventListener()和removeEventListener(),IE9+、FireFox、Safari、Chrome和Opera都是支持DOM2级事件处理程序的。
对于IE8-,则使用的是IE专有的事件处理程序:两个类似的方法——attachEvent()与detachEvent()。
事件绑定方式三【DOM 2级事件处理程序 / 监听事件】:addEventListener()和removeEventListener()监听事件接收3个参数:事件类型(注:不要加 on), 处理函数 , false:冒泡/true:捕获
IE中:attachEvent()和detachEvent()监听事件接收2个参数:事件类型(注:要加 on), 处理函数//只支持冒泡
例:
<input type="button" name="eventBtn3" id="eventBtn3" value="事件绑定方式三" />
/*------事件绑定方式三【DOM 2级事件处理程序 / 监听事件】------*/
var Btn3 = document.getElementById("eventBtn3");//给谁绑定事件,就要先获取谁
//添加监听事件1:【内嵌方式】
Btn3.addEventListener('click' , function(){
alert("我是事件绑定方式二:DOM 2级事件处理程序");
} , false) //false:冒泡,true:捕获
//添加监听事件2:【调用方式】
Btn3.addEventListener('click' , addevFn , false);
function addevFn(){
alert("我是事件绑定方式二:DOM 2级事件处理程序 【调用方式】");
}
//添加不同的监听事件:鼠标移入事件【调用方式】
Btn3.addEventListener('mouseover' , addevFn2 , false);
function addevFn2(){
alert(this.value);
}
//*删除监听事件【注:删除时事件类型名、事件函数名要一一对应哦(就是和添加事件时的参数一样)】
Btn3.removeEventListener("mouseover" , addevFn2, false)
//IE8及以下的添加和删除监听事件方法:(注:IE9及以上的就用上面的方法啦)
//IE8及以下的添加监听事件1:【内嵌方式】 (注 attachEvent 和 detachEvent方法只传两个参数,前面两个和上面一样,而第3个参数是因为在IE中默认就是冒泡方式,所以不用传第3个参数啦)
Btn3.attachEvent("onclick" , function(){
alert("我是IE8及以下的添加监听事件方法,【内嵌方式】");
});
//IE8及以下的添加监听事件2:【调用方式】(注:attachEvent 和 detachEvent 事件类型前而一定要加 on 才可以哦)
Btn3.attachEvent("onclick" , addevFn3);
function addevFn3(){
alert("我是IE8及以下的添加监听事件方法,【调用方式】");
}
//IE8及以下的删除监听事件
Btn3.detachEvent("onclick" , addevFn3);
事件方面性能优化:事件委托和事件处理程序的移除
在JavaScript代码当中,添加到页面中的事件越多,页面的性能也就越差。导致这一问题的原因主要有:
每个函数都是对象,都会占用内存。内存中对象越多,性能也就越差。
必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
为了进行页面的性能优化,因此我们会采用两种方法,就是上面提到的——事件委托和事件处理程序的移除。
事件委托:
什么时候使用事件委托,其实,简单来说,当时一个页面事件处理程序比较多的时候,我们通常情况下会使用它。
事件委托主要利用了事件冒泡,只指定一个事件处理程序,就可以管理一个类型的所有事件。例如:我们为整个一个页面制定一个onclick事件处理程序,
此时我们不必为页面中每个可点击的元素单独设置事件处理程序
事件委托:给元素的父级或者祖级,甚至页面绑定事件,然后利用事件冒泡的基本原理,通过事件目标对象进行检测,然后执行相关操作。其优势在于:
大大减少了事件处理程序的数量,在页面中设置事件处理程序的时间就更少了(DOM引用减少——也就是上面我们通过id去获取标签,所需要的查找操作以及DOM引用也就更少了)。
document(注:上面的例子没有绑定在document上,而是绑定到了父级的div上,最为推荐的是绑定在document上)对象可以很快的访问到,而且可以在页面生命周期的任何时点上为它添加事件处理程序,
并不需要等待DOMContentLoaded或者load事件。换句话说,只要可单击的元素在页面中呈现出来了,那么它就立刻具备了相应的功能。
整个页面占用的内存空间会更少,从而提升了整体的性能。
移除事件处理程序
每当将一个事件处理程序指定给一个元素时,在运行中的浏览器代码与支持页面交互的JavaScript代码之间就会建立一个连接。连接数量也直接影响着页面的执行速度。
所以,当内存中存在着过时的“空事件处理程序”的时候,就会造成Web应用程序的内存和性能问题。
那么什么时候会造成“空事件处理程序”的出现呢?
文档中元素存在事件,通过一些DOM节点操作(removeChild、replaceChild等方法),移除了这个元素,但是DOM节点的事件没有被移除。
innerHTML去替换页面中的某一部分,页面中原来的部分存在事件,没有移除。
页面卸载引起的事件处理程序在内存中的滞留。
解决方法:
合理利用事件委托;
在执行相关操作的时候,先移除掉事件,再移除DOM节点;
在页面卸载之前,先通过onunload事件移除掉所有事件处理程序。