JavaScript

JS中的事件

字数:10245    阅读时间:52min
阅读量:4446

JS事件就是发生在DOM元素、document对象、window对象上的一些特定的交互瞬间(比如点击一个元素发生onclick事件);JS和HTML的交互就是通过事件来实现的;

事件流

事件流就是描述页面元素接受事件的顺序;
冒泡流:就是由目标元素逐级向上传播一直到根节点,是由IE制定的事件触发顺序;所有浏览器都支持事件冒泡;(有的事件会跳过事件冒泡,例如 focus和blur事件);一般在事件冒泡阶段会触发目标事件
捕获流:从根节点开始逐级向下传播一直到目标元素,是由网景公司提出的事件触发顺序;事件捕获比冒泡更快一点,提升浏览器性能(在移动端的touch事件常用),IE9以下不支持事件捕获;在为了提升浏览器性能,现代浏览器也都会在捕获阶段触发目标事件
W3C考虑到IE所占的市场份额,就将IE主张的冒泡流和网景公司主张的捕获流综合了一下,于是DOM2级事件就分为了三个阶段:捕获阶段、目标阶段、冒泡阶段;

事件示意图

事件处理程序

事件是用户或者浏览器自身执行的某种动作,而响应事件的那个函数就是事件处理程序;为元素添加事件的方式有三种:HTML内联式、属性绑定、事件监听;那么对应的事件处理程序也有三种方式HTML事件处理程序、DOM0事件处理程序、DOM2事件处理程序

HTML事件处理程序 :直接在DOM节点上定义要执行的事件和事件处理程序;缺点:1.事件处理程序直接写在HTML里,需要注意一些特殊字符的转义;2.事件处理程序写在js文件里,当页面加载的时候节点会先加载出来,但是事件处理程序不一定加载出来;3.事件绑定在html上,事件处理程序写在js文件里,修改代码的时候会很麻烦而且结构和行为的耦合度会比较高,不易维护;所以真正开发基本不会用到HTML事件绑定和处理

DOM0事件处理程序:就是属性绑定的事件的执行函数;注意:执行函数的this指向的是当前触发它的dom对象;每个执行函数都有一个event参数,它是当前事件的局部事件对象。DOM0级事件处理程序解决了HTML事件处理程序的缺点,但是也存在几个缺点:1.一个节点只能定义一个同名事件处理程序,多了会被覆盖;2,不能选择事件是在捕获阶段触发还是在冒泡阶段触发

DOM2级事件处理程序:就是利用事件监听进行事件处理;IE9及以上和现代浏览器使用addEventlistener()和removeEventListener();IE9以下使用attachEvent()和detachEvent();

  • addEventlistener()有三个参数 (this指向当前触发事件的DOM对象;执行顺序:谁先定义谁先执行)
    1. 第一个参数是要执行事件的事件名(不带on;eg:click)
    2. 第二个参数是事件处理函数
    3. 第三个参数一般是一个布尔值,true表示在捕获阶段触发事件,false表示在冒泡阶段触发事件(默认值是false);也可以是一个对象,这个对象有三个属性对应的也是布尔值(默认值都是false)
      • capture:false 表示在冒泡阶段触发事件
      • once:true 表示事件处理程序只执行一次
      • passive:true 表示事件执行程序永远不会调用preventDefault(),可以安心的运行,有利于提升浏览器性能,解决滚动或者缩放时候的卡顿等现象。即使在事件执行程序里调用里preventDefault()也会被忽略
  • IE9以下事件监听程序attachEvent()
    • 接收两个参数;第一个参数是要执行事件的事件名(带on;eg:onclick);第二个参数是要处理事件的执行函数
    • this指针指向的是全局作用域
    • 他的执行顺序是反着的;最后定义的事件监听最先执行,最先定义的事件监听最后执行

事件优先级:1).定义在HTML上的事件会被DOM0和DOM2事件覆盖;DOM0和DOM2本质上都是属性事件,谁先定义谁先触发;2).按照事件的阶段来说捕获阶段的事件会最先触发,HTML事件和DOM0事件都只有目标阶段和冒泡阶段随后触发(按照事件定义的先后顺序触发);3).事件的执行顺序是依照HTML的DOM树顺序进行触发的,与CSS没有关系;(点击事件在移动设备上的事件穿透行为可能会影响到事件的触发顺序)

事件对象

事件在触发的时候会产生一个event事件对象,这个对象种包含着所有与该事件的有关信息;所有浏览器都支持事件对象,但是支持的方式不同;每一种事件对象上的属性和方法都不一样,但是都有以下这些共有的属性和方法

  • 事件对象的一些属性
    • target 派发事件的目标对象(触发事件的源对象)
    • currentTarget 当前触发事件的对象;与this时刻保持一致
    • type 事件的类型(eg:click)
    • cancelable 事件能否取消默认行为
    • eventPhase 事件流正在经历的阶段 1---捕获阶段,2---目标阶段,3---冒泡阶段
    • defaultPrevented 是否调用了preventDefault()
    • trusted 当前事件是否为浏览器自带事件
    • timeStamp 事件发生在距离打开计算机的毫秒数
  • 事件对象的一些方法
    • stopPropagation() 取消事件冒泡
    • preventDefault() 取消事件默认行为
    • stopImmediatePropagation() 取消事件的冒泡,同时阻止任何定义在其后边事件处理程序

事件对象的兼容处理:IE外浏览器的事件对象可以通过事件处理程序的参数获得(事件源参数对象),IE浏览器的事件对象是window的一个属性,需要写在事件处理程序中window.event;function(ev){var event = ev || window.event}

阻止事件冒泡行为:IE外浏览器:event.stopPropagation();IE浏览器:event.cancelBubble = true

阻止事件默认行为:IE外浏览器:event.preventDefault();IE浏览器:event.returnValue = false

兼容事件处理程序及事件对象的一些方法

事件类型

UI与焦点事件

  • UI事件:用户与页面上的元素进行交互的时候触发(不一定与用户操作有关)
    • load:页面(图像,JS文件,CSS文件等外部资源)完全加载后触发;(支持load的标签有img,script,link,video,audio等)
    • unload:当页面完全卸载之后触发;(例如用户从当前页面切换到其他页面就会触发)
    • resize:窗口大小发生变化的时候触发
    • scroll:滚动滚动条的时候触发
    • error:JavaScript发生错误的时候触发
    • abort:正在加载资源被终止的时候触发
    • DOMContentLoaded:页面形成完整的DOM树之后就触发,不理会图片JS、CSS外部文件等资源是否已下载完毕;H5新增, IE9+,safari3.1+,opera9+, firefox,chrome
    • beforeunload:页面卸载之前触发;H5新增, IE,safari,opera11+, firefox,chrome
    • haschanged:URL中哈希值发生变化的时候触发;event对象额外多两个属性:oldURL,newURL保存变化前后的完整URL(只有firefox6+,chorme,opera支持,所以还是使用location.hash来记录的好);H5新增, IE8+,safari5+,opera10.6+, firefox3.6+,chrome
    • readystatechange:文件或者元素的加载状态发生变化的时候触发,多用于Ajax请求;H5新增
      • readystatechange事件对象event新增readyState属性
        • uninitialized:未初始化
        • loading:正在加载数据
        • loaded:数据加载完成
        • interactive:可以进行交互
        • complete:加载完成
    • pageshow和pagehide:用户使用浏览器前进或者后退的时候触发;event对象新增persisted(页面是否通过缓存加载);H5新增 IE10+,safari5+,opera, firefox,chrome
  • 焦点事件:在页面元素获得或者失去焦点的时候触发;通常配合document.hasFocus()页面时候获得焦点和document.activeElement()获得焦点的元素得知用户在页面上的行踪
    • blur:元素失去焦点的时候触发;不会冒泡
    • focus:元素获得焦点的时候触发;不会冒泡
    • focusin:元素获得焦点触发;冒泡;IE5.5+,safari5.1+,opera11.5+,chrome
    • focusout:元素失去焦点触发; IE5.5+,safari5.1+,opera11.5+,chrome

鼠标与滚轮事件

  • 鼠标点击相关事件
    • click:点击鼠标主键触发(一般是时左键)
    • dblclick:双击鼠标主键触发(鼠标事件的触发顺序:mousedown-mouseup-click-mousedown-mouseup-click-dblclick;IE8会跳过第二次的mousedown和click)
    • mousedown:鼠标按键按下触发
    • mouseup:鼠标按键抬起触发
    • contextmenu:点击鼠标的副键时触发(一般是右键) H5新增
  • 鼠标移动相关
    • mouseover:鼠标移入时触发(移入子节点也触发);有冒泡行为
    • mouseout:鼠标移出时触发(移出子节点也触发);有冒泡行为
    • mousemove:鼠标在元素上移动的时候触发
    • mouseenter:鼠标进入时触发(进入子节点不触发);无冒泡
    • mouseleave:鼠标离开时触发(离开子节点不触发);无冒泡
  • 鼠标滚轮
    • 不考虑浏览器兼容(IE9+)可以直接使用wheel事件,deltaY表示垂直滚动的距离;
    • IE,opera,chrome,safari等用mousewheel事件表示事件滚动;wheelDelta表示一次滚动多少,一般向上120,向下-120 ( 数值不固定 ,opera9.5之前上下滚动得到的数值正负号是颠倒的);
    • Firefox有自己特有的DOMMouseScroll事件来表示滚轮事件;detail表示一次滚动多少向上-3向下3;
    • 兼容处理:将mousewheel事件和DOMMouseScroll事件都写上;
  • 鼠标事件对象event的一些属性
    • 鼠标位置位置相关
      • screenX(screenY) 鼠标相对于整个电脑屏幕的左上角的距离
      • clientX(clientY) 鼠标相对于可视区域(浏览器窗口)左上角的距离
      • pageX(pageX) 鼠标点击相对于整个页面左上角的距离(IE9+和现代浏览器); 兼容IE8:clientX + scrollLeft - clientLeft
      • layerX(layerX) 鼠标当前位置距离触发对象左上角的距离;以border为参考点
      • offsetX(offsetX) 鼠标当前位置距离触发对象左上角的距离;以content为参考点
    • 按键按钮相关
      • ctrlKey 鼠标点击的时候是否按下了ctrl键;布尔值
      • shiftKey 鼠标点击的时候是否按下了shift键;布尔值
      • aultKey 鼠标点击的时候是否按下了ault键;布尔值
      • metaKey 鼠标点击的时候是否按下了meta键(window键);布尔值
      • button 0表示主键,1表示中建,2表示副键;但是在IE8及以下版本,1表示主键,4表示中建,2表示副键
      • buttons 同时按下了鼠标的那几个键的键值和

键盘与文本事件

  • 键盘事件
    • keydown 用户按下任意键时触发;按下不松会重复触发
    • keypress 用户按下字符键时触发; 按下不松会重复触发
    • keyup 用户抬起按键时触发
  • 键盘事件对象Event属性
    • keyCode:按下键的键码值;(keypress事件也可以用charCode属性获取ASCII编码);获得键码或者编码之后可以通过String.fromCharCode(event.keyCode)来获得实际按下的字符
    • shiftKey、ctrlKey、altKey、metaKey(IE不支持metaKey,FF有问题):快捷键是否按下;布尔值
  • 文本事件(DOM3新增,只有IE9+,safari,chrome支持)textInput 当用户再可编辑区域输入字符时触发该事件(自己测试IE也不支持)
  • 文本事件对象Event属性data:按下的那个键的字符

设备、触摸与手势事件

  • 设备事件 可以让开发人员确定用户怎样在使用设备的一些事件
    • orientationchange事件 手机旋转方向时触发;该事件的window.orientation属性中包含3个值:0表示肖像模式、90表示左旋转的横向模式、-90表示右旋转的横向模式 ;(在事件执行函数里直接调window.orientation,event对象里什么都没有)
    • deviceOrientationEvent事件 主要是用来通过在x、y、z方向来告诉开发者设备的空间朝向
      • 触发deviceOrientationEvent事件时event包含:
        • alpha:围绕z轴旋转,y轴的度数差;0~360的值
        • beta:围绕x轴旋转,z轴的度数差;-180~180的值
        • gamma:围绕y轴旋转,z轴的度数差;-90~90的值
        • absolute:表示设备是否返回一个绝对值
        • compassCalibrated:表示设备的指南针是否校准过
    • devicemotion事件 主要是用来告诉开发者设备什么是什么时候移动的;可以用来判断移动设备是不是在往下掉,或者是不是被走着的人拿在手里
      • 触发devicemotion事件时event包含:
        • acceleration:包含x、y、z属性的对象,不考虑重力的情况下,每个方向的加速度
        • accelerationIncludingGravity:包含x、y、z属性的对象,在考虑自然重力加速度的情况下,每个方向上的加速度
        • interval:毫秒值,必须在另一个devicemotion事件触发前传入
        • rotationRate:包含表示方向的alpha、beta和gamma属性的对象
  • 触摸事件 (触摸屏幕时发生事件的顺序:touchStart-mouseover-mousemove-mousedown-mouseup-click-touchend)
    • touchstart:触摸屏幕时触发
    • touchmove:滑动时触发,调用preventDefault()函数可阻止滚动
    • touchend:当手指从屏幕上移开时触发
    • touchcancel:当系统停止跟踪触摸时触发
  • 触摸事件对象event增加属性
    • touches:表示当前跟踪的触摸操作的Touch对象的数组
    • targetTouches:特定于事件目标的Touch对象的数组
    • changedTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组
    • 每个touch对象又有以下属性
      • clientX:视口中的x坐标
      • pageX:触摸目标在页面中的x坐标
      • screenX:触摸目标在屏幕中的x坐标
      • target:触摸DOM节点目标
      • identifier:标识触摸的唯一ID
  • 手势事件 现在只有ios设备支持
    • gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发
    • gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发
    • gestureend:当任何一个手指从屏幕上面移开时触发
  • 手势事件对象event增加属性
    • rotation:表示旋转的角度,从0开始,负值表示逆时针旋转,正值表示顺时针旋转
    • scale:表示两个手指间距离的变化,从1开始,随着距离拉大而增加,随着距离缩小而减小

变动事件和MutationObserver对象

  • 变动事件 DOM结构发生变化的时候触发的事件 (IE8及以下版本不支持)
    • DOMSubtreeModified DOM结构发生任何变化时都触发
    • DOMNodeInserted 插入节点的时候触发
    • DOMNodeRemoved 移除节点的时候触发
    • DOMNodeInsertedIntoDocument 插入文档的时候触发
    • DOMNodeRemovedFromDocument 节点被直接从文档中删除的时候触发
    • DOMAttrModified 在特性被修改的之后触发
    • DOMCharacterDataModified 文本节点的值发生变化的时候触发

变动事件Mutation events,主要是监听节点的变化做一些事情;但是它存在着一些问题:1).浏览器支持情况不是特别好,webkit内核浏览器不支持DOMAttrModified而且自己测试IE和FF都不支持Mutation events了;2). Mutation events同步加载的,每次调用变动事件都会从事件队列中取出事件、执行事件、移除事件,频繁改动事件队列会造成浏览器性能下降;3).变动事件采用的是冒泡流,在冒泡阶段又多次触发其他变动事件,可能会阻塞JS进程,导致浏览器崩溃;为此DOM4为我们提供了MutationObserver对象来替代Mutation events

MutationObserver对象会将变动记录放在一个数组里,当文档加载结束后再统一执行变动事件;是异步加载的;可以观察整个DOM也可以观察某一类DOM;浏览器支持情况:IE11+、edge12+、opera15+、safari7+、firefox14+,chrome26+

  • MutationObserver对象上的方法
    • observe(DomNode,options) 启动观察,设置观察的对象和观察内容
      • DomNode:要观察的节点
      • options:要观察什么的配置对象
        • childList :观察子元素变动(增、删、改)
        • attributes:观察属性变动
        • characterData:观察节点内容或者文本节点内容变动
        • subtree:是否观察所有子节点及后代节点的变动;布尔值;(不能单独使用,需配合一种设置属性使用)
        • attributeOldValue:attributes变动时是否记录之前的属性值;布尔值
        • characterDataOldValue: characterData变动时是否记录之前的值;布尔值
        • attributesFilter:需要观察的特定属性的数组;值为数组
    • disconnect() 停止观察;触发之后,Dom的变化就不会再被观察记录了
    • takeRecords() 清除变动记录, 即不再处理未处理的变动
  • MutationRecord对象的属性 (跟事件对象event很像,与event不同的是它是由多个MutationRecord实例组成的数组) Dom每发生一次变化就产生一个MutationRecord实例对象,这个对象里包含着所有与变动相关的信息,mutationObserver处理的就是一个个mutationRecord组成的实例数组
    • type:观察变动的类型(childList、attributes...)
    • target:发生变动的DOM对象
    • addedNodes:新增的DOM对象
    • removedNodes:删除的DOM对象
    • previousSibling:前一个同级节点,没有返回null
    • nextSibling:后一个同级节点,没有返回null
    • attributeName:发生变动的属性;如果设置了attributeFilter就只返回预先指定的属性
    • oldValue:变动前的值;只对attributes和characterData的变动有效

内存和性能

事件可以增强浏览器交互能力;但是每一个函数都是一个对象,都需要占用一定内存,内存中对象越多性能就越差;还有就是事先定义的事件处理程序访问DOM节点,会延迟页面的就绪时间,也就是加长了页面加载时间;这样总是不好的,所以我们需要尽可能的优化我们的事件

事件委托

我们知道一般的事件都会向上冒泡,逐次向上层元素触发该类事件,那么我们是不是可以在同类型的事件的最上层DOM节点上定义这个事件,然后判断当前点击对象再做出对应的响应;这就是事件委托;将相同的事件类型委托给最外层的节点,通过冒泡的方式触发,从而减少事件处理程序的定义;如果可以的话最好直接定义到document元素上,这样既节省了内存空间和页面加载时间而且可以在页面的任何生命周期快速添加事件处理程序; 最适合采用事件委托的事件包括:click、mousedown、mouseup、keydown、keyup 和 keypress

删除事件

在我们确定不需要某个事件的时候,我们就需要将这个事件及时的移除掉,这样有利于浏览器的性能;一般是在删除元素removeChild()或者replaceChild()元素被删除,或者使用innerText替换掉了原先有事件的节点,这个时候事件就没用了,就可以移除了;还有就是页面卸载之后事件就都没用了,但是浏览器不一定能把所有事件处理程序都清理干净,特别是IE8及以前的浏览器,会留下比较多未被清理的事件处理程序在内存中,导致内存被无故占用,可以使用unload事件在页面彻底卸载之前把所有事件处理程序都清理了

模拟和自定义事件

模拟事件

JS模拟事件要分三个步骤:1.创建事件对象event; 2.初始化事件对象;3.触发事件

1.创建事件对象event:document为我们提供了一个createEvent()方法来创建event对象;此方法接收一个参数,即要创建的事件类型;可以是以下几种类型直以

  • UIEvents:一般化的UI事件,鼠标事件和键盘事件都继承自UI事件,DOM3级中是UIEvent
  • MouseEvents:一般化的鼠标事件,DOM3级中是MouseEvent;
  • MutationEvents:一般化的DOM变动事件,DOM3级中是MutationEvent
  • HTMLEvents:一般化的HTML事件,没有对应的DOM3级事件,被分散到 其他类别中

2.初始化事件对象:使用与事件有关的信息对其进行初始化,每种类型的event对象都有一个特殊的方法,为它传入适当的数据就可以初始化该event对象,不同类型的方法的名字也不相同(一般是init开头的),具体要取决于createEvent()中使用的参数

3.触发事件 :最后调用dispatchEvent()方法,此方法接收一个参数,即要触发事件的event对象。

  • 模拟鼠标事件的事件对象包含属性
    • type:事件类型
    • bubble:是否冒泡
    • cancelable:是否可取消默认行为
    • view:与事件关联的视图,一般为document.defaultView
    • detail:一般为0,一般只有事件处理程序使用
    • screenX:事件相对于屏幕的X坐标
    • clientX:事件相对于视口的X坐标
    • ctrlKey、 altKey 、 shiftKey 、 metaKey :是否按下了相应的键,默认为false
    • button:表示按下了哪个鼠标键,默认为0
    • relatedTarget:表示与事件相关的对象。一般只有在模拟mouseover与mouseout时使用
  • 模拟键盘事件的事件对象包含的属性
    • type:事件类型
    • bubbles:是否应该冒泡
    • cancelable:是否可以取消默认行为
    • view:与事件关联的视图。一般为document.defaultView
    • key:按下的键的键码
    • location:表示按下哪里的键。0为主键盘;1为左;2为右;3为数字键盘;4为虚拟键盘;5为手柄
    • modifiers:空格分隔的修改键列表,如“shift”
    • repeat:在一行中按下了多少次这个键

自定义事件

我们既然可以模拟事件,当然也可以自定义一个事件;与模拟事件步骤一样1.调用document.createEvent('CustomEvent')创建一个event对象2.初始化这个event对象所包含initCustomEvent对象,这个对象接收四个参数;3. 调用dispatchEvent()方法,触发定义的事件

  • 自定义的event事件对象包含的四个参数
    • type:事件类型;字符串
    • bubbles:时间是否应该冒泡;布尔值
    • cancelable:表示事件是否可以取消;布尔值
    • detail:任意值,保存在event对象的detail属性中;对象
野生小园猿
励志做一只遨游在知识海洋里的小白鲨
查看“野生小园猿”的所有文章 →

相关推荐