关于javascript:JS事件简单总结

4次阅读

共计 4772 个字符,预计需要花费 12 分钟才能阅读完成。

什么是事件

这个其实不是很好形容,有可能是由用户发动的,比方鼠标事件,键盘事件,窗口事件,表单事件,也有可能是页面加载资源过程中的事件。常见的事件比方: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-10
element.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 target
parent dom0 js target
parent bubble target 

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

parent capture capture
child dom0 html target
child bubble target
child capture target
parent dom0 js bubble
parent 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 指向以后的元素。

自定义事件

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

正文完
 0