JavaScript事件【event】
在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事件移除掉所有事件处理程序。
转载于:https://blog.51cto.com/mupiao/1859116