之前对事件都是能用就行,本周对事件进行了一下较全面的学习, 在这里记录一下。javascript
事件能够理解为行为,如对button的点击,事件的本质是一个函数,他接收一个event对象。
每一个事件会产生一个Event对象
,Event 对象表明事件的状态,好比事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。html
事件的传播机制能够分为冒泡
和捕获
java
官方的定义就是从最特定的事件目标到最不特定的事件目标。express
意思就是说,假如用户单击了一个元素,该元素拥有一个click事件,那么一样的事件也将会被它的祖先触发,这个事件从该元素开始一直冒泡到DOM树的最上层,这一过程称为事件冒泡
事件捕获和事件是相反的,也就是说,当用户触发了一个事件的时候,这个事件是从DOM树的最上层开始触发一直到捕获到事件源。
事件流是事件传播机制的标准segmentfault
因为微软和网景乱搞,后来必需要为事件传播机制,制定一个标准,由于事件捕获是网景公司开发出来的,而事件冒泡是由微软公司开发出来的,它们都想要本身的技术成为标准,因此致使这两个公司总是干架,制定标准的人为了避免让它们干架,因此研发了事件流。
事件流的使用就是咱们经常使用的addEventListener
了,标准用法以下数组
dom对象.addEventListener(事件类型, 回调函数, 事件机制)
这里的事件类型表示你要使用哪一种事件类型好比click
, 回调函数里面写着触发此事件你要作什么事情, 事件机制分为冒泡和捕获,若是为false
表示事件冒泡,为true
表示事件捕获
发现本身之前一直使用的都是不标准的写法缓存
dom对象.attachEvent(eventType, fn)
第一个参数表示事件类型,第二个是回调,这种的事件传播机制是冒泡
关于js的事件就到这里了,若是想要了解js的兼容写法等能够查看这篇文章。angular2
Angular 组件和 DOM 元素经过事件与外部进行通讯, Angular 事件绑定语法对于组件和 DOM 元素来讲是相同的 - (eventName)="expression" :dom
<button (click)="onClick()">Click</button>
angular 事件特色:ide
Angular 支持 DOM 事件冒泡机制,但不支持自定义事件的冒泡
下面咱们来看看angular是如何处理事件的,下面的源码阅读过程来自Angular 4.x EventManager & Custom EventManagerPlugin
Angular 在解析 DOM 树的时候,对于事件绑定它会调用DomRenderer
实例的listen()
方法,进行事件绑定,listen()
方法具体实现以下:
// angular2/packages/platform-browser/src/dom/dom_renderer.ts class DefaultDomRenderer2 implements Renderer2 { .... listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean): () => void { checkNoSyntheticProp(event, 'listener'); if (typeof target === 'string') { return <() => void>this.eventManager.addGlobalEventListener( target, event, decoratePreventDefault(callback)); } return <() => void>this.eventManager.addEventListener( target, event, decoratePreventDefault(callback)) as() => void; } }
经过源码咱们发现,无论走哪条分支,最终都是调用this.eventManager
对象的方法设置事件监听。这里的this.eventManager
是什么?它是 Angular 中的事件管理器EventManager
。
在 Angular 中全部的事件绑定都是由一个事件管理器来驱动,事件管理器自己由多个事件插件提供支持。Angular 中内置的事件插件以下:
// angular2/packages/platform-browser/src/dom/events/event_manager.ts export class EventManager { // EventManagerPlugin列表 private _plugins: EventManagerPlugin[]; // 缓存已匹配的eventName与对应的插件 private _eventNameToPlugin = new Map<string, EventManagerPlugin>(); constructor( @Inject(EVENT_MANAGER_PLUGINS) plugins: EventManagerPlugin[], private _zone: NgZone) { plugins.forEach(p => p.manager = this); /** * {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true}, * {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true}, * {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true} * * slice(): 建立新的plugins数组 * reverse(): 让DomEventsPlugin插件做为列表最后一项,由于它可以处理全部的事件。 */ this._plugins = plugins.slice().reverse(); } // 获取能处理eventName的插件,并调用对应插件提供的addEventListener()方法 addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { const plugin = this._findPluginFor(eventName); return plugin.addEventListener(element, eventName, handler); } // 获取能处理eventName的插件,并调用对应插件提供的addGlobalEventListener()方法 addGlobalEventListener(target: string, eventName: string, handler: Function): Function { const plugin = this._findPluginFor(eventName); return plugin.addGlobalEventListener(target, eventName, handler); } // 获取NgZone getZone(): NgZone { return this._zone; } /** @internal */ _findPluginFor(eventName: string): EventManagerPlugin { // 优先从_eventNameToPlugin对象中获取eventName对应的EventManagerPlugin const plugin = this._eventNameToPlugin.get(eventName); if (plugin) { return plugin; } // 遍历插件列表,判断当前插件是否支持eventName对应的事件名 const plugins = this._plugins; for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i]; if (plugin.supports(eventName)) { this._eventNameToPlugin.set(eventName, plugin); return plugin; } } throw new Error(`No event manager plugin found for event ${eventName}`); } }
_findPluginFor()
方法,查询对应的可以处理 eventName 对应的 EventManagerPlugin 插件对象。eventName
做为参数调用插件对象提供的supports()
方法,判断当前是否可以处理eventName
对应的事件。所以对于 EventManagerPlugin 插件对象,若是要声明可以处理某类事件,就须要在supports()
方法中进行相应处理。export abstract class EventManagerPlugin { constructor(private _doc: any) {} manager: EventManager; // 判断是否支持eventName对应的事件 abstract supports(eventName: string): boolean; // 添加事件监听 abstract addEventListener(element: HTMLElement, eventName: string, handler: Function): Function; // 添加全局的事件监听 addGlobalEventListener(element: string, eventName: string, handler: Function): Function { const target: HTMLElement = getDOM().getGlobalEventTarget(this._doc, element); if (!target) { throw new Error(`Unsupported event target ${target} for event ${eventName}`); } return this.addEventListener(target, eventName, handler); }; }
了解了事件的处理过程,是否疑惑咱们是否能够自定义事件处理过程?是的咱们能够,经过自定义EventManagerPlugin的方式,固然这应该是至关高端的操做了,有兴趣的能够看这篇文章
javascript event(事件对象)详解
Angular 4.x EventManager & Custom EventManagerPlugin
Angular 4.x Events Bubbling