YUI事件体系之Y.EventTarget
上兩篇文章YUI事件體系之Y.Do、YUI事件體系之Y.CustomEvent中,分別介紹了YUI實現AOP的Y.Do對象,以及建立自定義事件機制的Y.CustomEvent對象。
本篇文章,將要介紹YUI事件體系集大成者、最為精華的部分——Y.EventTarget。
Y.EventTarget
DOM事件中的目標元素為event.target,這類元素可以觸發、監聽一些事件,例如input元素的click、focus等事件。這也正是Y.EventTarget的命名淵源,它提供了一種讓任意對象定義、監聽、觸發自定義事件的實現方式。
從設計上看,Y.EventTarget通過內部維護一系列Y.EventCustom對象,提供了可以通過事件名稱進行事件定義、監聽和觸發的便捷接口。另外,推薦使用Y.augment將它以組合的方式加載在其它類上,而不要使用繼承。關于Y.augment和Y.extend之間的異同,可以參考我之前的一篇文章:Y.extend與Y.augment。
YUI很多基礎類都擴展了Y.EventTarget,重要的有Y(YUI instance,sandbox)、Y.Global、Y.Node、Y.NodeList、Y.Base等。
YUILibrary有專門一個章節介紹EventTarget,非常詳盡,如果你對EventTarget的設計思路和使用方法感興趣,請移步YUILibrary-EventTarget。
示例
首先,讓我們看看Y.EventTarget獨立調用的例子:
// 例1 YUI().use('event-custom', function (Y) {var et = new Y.EventTarget();et.on('say', function (msg) {console.log('say:', msg);});et.on('listen', function (msg) {console.log('listen:', msg);});// output: say: Hello, worldinstance.fire('say', 'Hello, world'); });這種方式實際意義不大,YUI中只有Y.Global使用了這種方式。
下面,讓我們看下最廣泛的使用方式,即通過Y.augment擴展其它類:
// 例2 YUI().use('event-custom', function (Y) {function MyClass() {}MyClass.prototype.add = function (item) {// do sththis.fire('addItem', { item: item });};MyClass.prototype.remove = function (item) {// do sththis.fire('removeItem', { item: item });};// 用EventTarget擴展MyClassY.augment(MyClass, Y.EventTarget);var instance = new MyClass();// 監聽addItem事件instance.on('addItem', function (data) {console.log('add an item:', data.item);});// 監聽removeItem事件instance.on('removeItem', function (data) {console.log('remove an item:', data.item);});// output: add an item: orangeinstance.add('orange');// output: remove an item: redinstance.remove('red'); });源代碼分析
接下來,讓我們看看YUI的內部實現吧。
注:為了更容易的看懂代碼的核心,我做了適當的簡化,感興趣的朋友可以去看未刪節的源碼。
var AFTER_PREFIX = '~AFTER~';// EventTarget構造器 var ET = function (opts) {var o = opts || {};// 私有事件聚合器this._yuievt = this._yuievt || {id: Y.guid(),events: {},config: o,// 默認配置defaults: {context: o.context || this,host: this,emitFacade: o.emitFacade,fireOnce: o.fireOnce,queuable: o.queuable,broadcast: o.broadcast}}; };ET.prototype = {constructor: ET,// 創建事件publish: function (type, opts) {var ce,events = this._yuievt.events,defaults = this._yuievt.defaults;ce = events[type];if (ce) { // 已創建過該事件if (opts) {ce.applyConfig(opts, true);}} else { // 基于CustomEvent,創建新事件ce = new Y.CustomEvent(type,(opts) ? Y.merge(defaults, opts) : defaults);events[type] = ce;}return ce;},// 監聽事件on: function (type, fn, context) {var ce,after,handle,args = null;// 判斷是否為后置監聽if (type.indexOf(AFTER_PREFIX) > -1) {after = true;type = type.substr(AFTER_PREFIX.length);}// 獲取自定義事件對象,如果未創建則先創建ce = this._yuievt.events[type] || this.publish(type);if (arguments.length > 3) {args = Y.Array(arguments, 3, true);}// 調用自定義事件對象的_on方法監聽事件handle = ce._on(fn, context, args, after ? 'after' : true);return handle;},// 監聽一次事件once: function () {var handle = this.on.apply(this, arguments);if (handle.sub) {// 監聽器執行一次則失效handle.sub.once = true;}return handle;},// 后置監聽事件after: function (type, fn) {var a = Y.Array(arguments, 0, true);a[0] = AFTER_PREFIX + type;return this.on.apply(this, a);},// 后置監聽一次事件onceAfter: function () {var handle = this.after.apply(this, arguments);if (handle.sub) {handle.sub.once = true;}return handle;},// 觸發事件fire: function (type) {var ce,args;args = Y.Array(arguments, 1, true);ce = this._yuievt.events[type];// 尚未創建事件if (!ce) return true;return ce.fire.apply(ce, args);},// 注銷事件監聽detach: function (type, fn, context) {var events = this._yuievt.events,ce,i;// 未設置事件類型,則注銷所有類型的事件if (!type) {for (i in events) {if (events.hasOwnProperty(i)) {events[i].detach(fn, context);}}return this;}ce = events[type];if (ce) {ce.detach(fn, context);}return this;} };進階用法
Y.EventTarget作為一個十分重要的類,提供了非常豐富、方便的使用方式,除了依賴內部Y.CustomEvent實現的事件接口、默認執行方法、事件廣播等,其余主要有:
a) 事件冒泡
多個EventTarget對象之間可以建立一定事件傳播關系,類似DOM事件中的冒泡。
// 例3 YUI().use('event-custom', function (Y) {// 父類function Parent() { ... }Y.augment(Parent, Y.EventTarget, true, null, { emitFacade: true });// 子類function Child() { ... }Y.augment(Child, Y.EventTarget, true, null, { emitFacade: true });var parent = new Parent(),child = new Child();// 子類對象添加冒泡目標對象,child -> parentchild.addTarget(parent);parent.on('hear', function (e) {console.log('parent hear', e.msg);});child.on('hear', function (e) {console.log('child hear', e.msg);});// output: child hear Hi, parent hear Hichild.fire('hear', { msg: 'Hi' }); });b) 事件前綴
在事件冒泡的基礎上,考慮到區分不同EventTarget對象觸發相同事件,YUI引入了事件前綴(Event Prefix)。
// 例4 YUI().use('event-custom', function (Y) {// 父類function Parent() { ... }Y.augment(Parent, Y.EventTarget, true, null, {emitFacade: true,prefix: 'parent' // 配置事件前綴});// 子類function Child() { ... }Y.augment(Child, Y.EventTarget, true, null, {emitFacade: true,prefix: 'child' // 配置事件前綴});var parent = new Parent(),child = new Child();child.addTarget(parent);parent.on('hear', function (e) { // 不能捕捉到child的hear事件console.log('parent hear', e.msg);});child.on('hear', function (e) {console.log('child hear', e.msg);});// output: child hear Hichild.fire('hear', { msg: 'Hi' });parent.on('*:see', function (e) { // 要想監聽到其它EventTarget對象的see事件,需要設置prefixconsole.log('parent see', e.thing);});child.on('child:see', function (e) { // 等同監聽see事件console.log('child see', e.thing);});// output: child hear MM, parent see MMchild.fire('see', { thing: 'MM' }); });參考
- YUILibrary-CustomEvent
- YUILibrary-EventTarget
總結
以上是生活随笔為你收集整理的YUI事件体系之Y.EventTarget的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最全Java面试208题,涵盖大厂必考范
- 下一篇: UAS-点评侧用户行为检索系统