什么是事件

这个其实不是很好形容,有可能是由用户发动的,比方鼠标事件,键盘事件,窗口事件,表单事件,也有可能是页面加载资源过程中的事件。常见的事件比方:click, dbclick, keydown, keypress, keyup, mousemove, wheel, scroll, focus, blur, load, unload, abort, error, resize, change, select, submit, 大略这些及其相干的。

事件流传机制

规范的事件是怎么触发以及流传的呢?

总体来说,事件流传分为三个阶段:

1. 事件捕捉阶段(capture phase),就是事件从window开始一层一层向指标传递的阶段
2. 指标阶段(target phase),当事件达到事件产生现场的阶段
3. 事件冒泡阶段(bubble phase),和事件捕捉阶段相同,事件从指标到window传递的阶段

IE8及其以下的事件只有冒泡,没有捕捉。

事件委托/代理

有个提到比拟多的就是事件委托。因为事件有冒泡机制,咱们能够不在指标元素监听事件,而在它的父元素监听。一个比拟经典的例子,一个领有很多子项的无序列表,须要监听子项上的点击事件。此时,咱们将监听绑定在父元素ul上,当点击li元素时,因为事件的冒泡机制,ul层也能触发点击事件。然而如何判断你点击的就是你想要的li元素,而不是ul元素自身,用事件对象event的指标元素属性target来判断就好了,即event.target。

这样做的长处有:进步性能,从监听多个事件缩小到监听一个事件,效率必定失去晋升;能动静地自适应,比方再在ul元素内加一个li子元素,传统的办法就须要再减少一个监听事件,而利用事件委托就能够以不变应万变。

事件实现形式

原始事件实现形式(DOM0)

也称DOM0事件处理形式,DOM0不是W3C的规范,因为历史倒退的起因,它存在过也始终存在着。很简略,只有在html元素中增加on+事件的属性即可。

<button id='myButton' onclick='doSomething()'> 按钮 </button>

或者用js的形式给元素增加这个属性。

var btEle = document.getElementById('myButton');btEle.onclick = doSomething; 

这样的长处是简略不便,并且兼容所有的浏览器;毛病也不少,违反了行为与体现拆散的准则,只能增加一个事件,也不能利用事件委托机制去更多的事。

IE事件实现形式

上文提到IE8及其以下的事件流传机制只有冒泡,没有捕捉,实现形式为

//监听事件element.attachEvent('on' + eventType, callback);//解除监听element.detachEvent('on' + eventType, callback);//手动触发事件,兼容性IE6-10element.fireEvent('on' + eventType) 

须要留神的是,匿名函数无奈被解除监听。

规范事件实现形式(DOM2)

DOM2规定的规范应该大一统了浏览器,而之后变得不多。目前W3C最新的规范是15年出的DOM4。咱们来简略看下。

//监听事件element.addEventListener(eventType, callback, useCapture);//解除监听element.removeEventListener(eventType, callback, useCapture);//手动触发事件element.dispatchEvent(eventType)//回调函数中有个event对象记录事件的属性 function callback(event){ //do something} 

因为规范的事件流传机制有捕捉和冒泡阶段,这里最初一个参数useCapture示意是否在事件捕捉阶段就执行回调函数,默认为false。个别状况下,咱们应用默认值,即在事件冒泡阶段执行函数。为什么?次要是为了兼容旧版的IE。

事件常见属性

咱们曾经晓得,事件的回调函数中有个事件对象的参数。从第一局部,咱们曾经看到Event对象的继承关系,比方咱们最常见的鼠标事件或者键盘事件,MouseEvent继承自UIEvent,UIEvent继承自Event。咱们参考最新的W3C标准,来具体看下。

[Constructor(DOMString type, optional EventInit eventInitDict),Exposed=(Window,Worker)]interface Event { // 事件的类型,比方click,submit,load等 readonly attribute DOMString type; // 触发事件的那个指标元素 readonly attribute EventTarget? target; // 事件流传所在的以后元素 readonly attribute EventTarget? currentTarget; const unsigned short NONE = 0; const unsigned short CAPTURING_PHASE = 1; const unsigned short AT_TARGET = 2; const unsigned short BUBBLING_PHASE = 3; // 事件所在的阶段,枚举值如上 readonly attribute unsigned short eventPhase; // 阻止事件的冒泡,在以后元素的冒泡执行完之后 void stopPropagation(); // 立刻阻止冒泡,包含以后元素还有其余的回调事件 // DOM3减少新属性  void stopImmediatePropagation(); // 是否在冒泡阶段 readonly attribute boolean bubbles; // 是否能够被勾销,用preventDefault勾销 readonly attribute boolean cancelable; // 勾销以后元素的默认事件,前提是cancelable为true void preventDefault(); // 是否被preventDefault过,DOM3减少新属性  readonly attribute boolean defaultPrevented; // 事件是否有用户触发的 [Unforgeable] readonly attribute boolean isTrusted; // 以后工夫戳 readonly attribute DOMTimeStamp timeStamp; void initEvent(DOMString type, boolean bubbles, boolean cancelable);};dictionary EventInit { boolean bubbles = false; boolean cancelable = false;}; [Constructor(DOMString type, optional MouseEventInit eventInitDict)]interface MouseEvent : UIEvent { // 绝对屏幕的x,y坐标 readonly attribute long screenX;  readonly attribute long screenY; // 绝对浏览器可视区域的x,y坐标 readonly attribute long clientX; readonly attribute long clientY; //是否按下这些非凡的键 readonly attribute boolean ctrlKey; readonly attribute boolean shiftKey; readonly attribute boolean altKey; readonly attribute boolean metaKey; //哪个鼠标键被按下,0主键,1滚轮,2附键,只在mouseup,mousedown事件中无效 readonly attribute short button; // 被激活的鼠标,能够是多个,0没有,1主键,2附键,4滚轮,8后退?,16后退? // 还能够相加,比方按下了主键和滚轮,就是1+4 = 5 readonly attribute unsigned short buttons;  readonly attribute EventTarget? relatedTarget;  boolean getModifierState(DOMString keyArg);}; 

以上是标准,但实际上浏览器的实现上如同还有很多杂七杂八的属性。以下是在Chrome 56输入的后果。

MouseEvent

感觉眼睛都要花了,比拟罕用的感觉不多,也看场景吧。个别比方target,preventDefalut,stopPropagation,type,以及坐标的属性比拟罕用吧。

因为浏览器的历史起因,对于鼠标坐标的属性真是好多对:

属性形容x, y在整个浏览器窗口所处的地位screenX, screenY在整个屏幕中所处于地位clientX, clientY在整个浏览器窗口所处的地位,与x, y属性雷同layerX, layerY在整个元素的区域鼠标的地位(但算上了scroll的间隔),当不随页面滚动而变动pageX, pageY在整个页面所处的地位,当不随页面滚动而变动offsetX, offsetY在整个元素的区域,鼠标的地位,当不随页面滚动而变动movementX, movementY两个事件之间鼠标的位移差

最好应用W3C标准外面的两对属性,是最平安和最保险的抉择,screenX, screenY, clientX, clientY。

简略的兼容实现

次要是思考对IE8及其以下的兼容性。

function addEventListener(target, event, callback) if( window.addEventListener ){ target.addEventListener(event, callback) } else if(window.attachEvent){ target.attachEvent('on' + event, callback); } else{ target['on' + event] = callback; }} 

事件的执行程序

咱们用一个简略的例子在Chrome 56中做测试。

当点击parent div的时候输入如下:

parent capture targetparent dom0 js targetparent bubble target 

而点击child div的时候输入如下:

parent capture capturechild dom0 html targetchild bubble targetchild capture targetparent dom0 js bubbleparent bubble bubble 

当你一直地调整那些事件的程序,或者引入js的onclick函数,你会发现以下法则:

1. 当html中有onclick事件,js中也有onclick事件的时候,js中onclick事件会笼罩html中的事件。因为其实它们是同一个属性。
2. 指标元素的事件执行程序跟定义的程序统一,而不会依照先捕捉后冒泡的程序。在html中定义的事件排在第一个。
3. 非指标事件的函数执行恪守先捕捉后冒泡的法则,并且DOM0级元素定义的事件会在冒泡阶段先执行,不论是在html定义还是用js定义。
</pre>

另外,如果一个事件屡次绑定,只会执行一次。

事件this指向

对于DOM0级的this状况如下。

而attachEvent中的this指向window,addEventListener指向以后的元素。

自定义事件

事件的机制形象进去就是公布订阅模式。咱们能够利用自定义事件去实现咱们本人想要的公布订阅模式。