之前對事件都是能用就行,本週對事件進行了一下較全面的學習, 在這裏記錄一下。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