Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Events
Search
ningzbruc
March 19, 2014
Programming
130
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Events
How to build a custom event system
ningzbruc
March 19, 2014
More Decks by ningzbruc
See All by ningzbruc
如何写出一个优秀的开源库
ningzbruc
0
68
去啊无线前端的一天
ningzbruc
1
200
React & Component
ningzbruc
0
47
阿里旅行去啊H5首页总结&Promise
ningzbruc
0
270
KISSY.Base - all about that Base
ningzbruc
0
140
Hammer.js
ningzbruc
1
340
淘宝旅行门票iPad版开发
ningzbruc
0
140
Travel on KISSY mini
ningzbruc
0
210
Why YUI3
ningzbruc
0
190
Other Decks in Programming
See All in Programming
アクセシビリティから考える情報設計
high_g_engineer
0
370
Heart of Swift Concurrency
koher
0
650
スマート反転とウェブアクセシビリティ
camiha
0
210
Security issues being discussed on Web Platforms
petamoriken
0
920
新人はどこまで自力でやり、どこからAIに頼るべきか/エンジニア育成に向き合う_先輩たちの悩みと知見共有会
toppan_digital_dev
1
620
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
1
550
[DroidKaigi 2026] Bring your own phones to Gradle Managed Devices
f2lk
0
120
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
450
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
270
GKE で Pod の見方を変えたら、スケールアウト時の挙動を真に捉えられた話
stkk
0
120
世界の中心で、AI(App Intents)をさけぶ ー App Intents中心設計の実践ガイド
touyou
0
550
cdk deploy JawsSonic #MARATHONしながらAWSリソースをデプロイしてみよう
akihisaikeda
2
130
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7.1k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
480
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.6k
Marketing to machines
jonoalderson
1
5.8k
Leo the Paperboy
mayatellez
10
2.3k
Git: the NoSQL Database
bkeepers
PRO
432
67k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
840
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
The Curious Case for Waylosing
cassininazir
1
510
Measuring & Analyzing Core Web Vitals
bluesmoon
9
990
Transcript
Events • DOM Events • Custom Events
# DOM Events
add button.addEventListener('click', doSomething, false);
remove button.removeEventListener('click', doSomething, false);
context button.addEventListener('click', obj.fn, false); button.addEventListener('click', obj.fn.bind(obj), false);
e • type • target/currentTarget • preventDefault • stopPropagation/stopImmediatePropagation •
cancelable/bubbles • …
Event Phases
target • target • currentTarget <ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li>
<li>five</li> <li>six</li> ul.on('click', doSomething);
delegate <ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> <li>five</li> <li>six</li> ul.delegate('click', 'li',
doSomething);
# Simulate Events
Simulate Events ! // 创建事件 var event = button.createEvent('Event');
! // 初始化事件 event.initEvent('click', true, true); ! // 触发事件 button.dispatchEvent(event);
None
Special Events • tripleclick • tap • valuechange • …
Special Events button.on('touchstart', function() { setTimeout(function()
{ var event = button.createEvent('Event'); event.initEvent('tap', true, true); button.dispatchEvent(event); }, 100); }); ! button.on('tap', doSomething); 触发tap事件
# Custom Events
None
Why • 低耦合 • 易通信
DOM Event Features • 绑定/移除 • 捕获/冒泡 • 阻⽌止默认⾏行为 •
阻⽌止传播 • …
How ! // 创建事件 var event = button.createEvent('Event');
! // 初始化事件 event.initEvent('whatever', true, true); ! // 触发事件 button.dispatchEvent(event);
None
flight.js ! var component = Component.attachTo('#container', cfg); ! component.on('select',
doSomething); ! component.trigger('select', { item: 'item' }); 模拟DOM事件
flight.js
None
What if not DOM-base Component?
None
Pub/Sub Pattern
Pub/Sub Pattern button.addEventListener('click', doSomething, false); publisher topic subscriber
Custom System • EventTarget - publisher • CustomEvent - topic
• Subscriber
Custom System EventTarget CustomEvent CustomEvent CustomEvent Subscriber Subscriber Subscriber
function EventTarget() { this._customEvents = {};
} ! // 绑定事件 EventTarget.prototype.on = function(type, fn, context) { this.getCustomEvent(type).add(fn, context); }; ! // 解除事件 EventTarget.prototype.detach = function(type, fn, context) { this.getCustomEvent(type).remove(fn, context); }; ! // 触发事件 EventTarget.prototype.fire = function(type, payload) { this.getCustomEvent(type).fire(payload); };
! function CustomEvent(host, type) { this._type
= type; this._subscribers = []; } ! // 绑定事件 CustomEvent.prototype.add = function(type, fn, context) { this._subscribers.push(new Subscriber(fn, context)); }; ! // 解除事件 CustomEvent.prototype.remove = function(type, fn, context) { this._subscribers.splice(this.indexOfSub(fn, context), 1); }; ! // 触发事件 CustomEvent.prototype.fire = function(type, payload) { this._subscribers.forEach(function(subscriber) { subscriber.fn.call(context, payload); }); };
function Subscriber(fn, context) { this.fn =
fn; this.context = context; //... more property }
! var component = new EventTarget(cfg); ! component.on('select', doSomething);
! component.fire('select', { item: 'item' }); ! component.detach('select', doSomething); 触发select事件
default action function CustomEvent(cfg) { this._type
= cfg.type; this._defaultFn = cfg.defaultFn; this._subscribers = []; } ! // 触发事件 CustomEvent.prototype.fire = function(type, payload) { this._subscribers.forEach(function(subscriber) { subscriber.fn.call(context, payload); }); this._defaultFn && this._defaultFn.call(this, payload); };
default action // 发布事件 EventTarget.prototype.publish = function(type, config) {
this.getCustomEvent(type, config); };
default action ! var component = new EventTarget(cfg); !
component.publish('show', { defaultFn: function() { component.container.css('display', 'block'); } }); ! component.on('show', doSomething); ! component.fire('show');
we love default action //选中⼀一个⽇日期 this.publish('select', {
defaultFn: this._defSelectFn }); ! //选中⼀一个⽇日期期间 this.publish('selectRange', { defaultFn: this._defSelectRangeFn }); ! //取消选中⼀一个⽇日期期间 this.publish('unselectRange', { defaultFn: this._defUnSelectRangeFn }); ! //选中⼀一个⽇日期期间开始 this.publish('selectRangeStart', { defaultFn: this._defSelectRangeStartFn }); ! //选中⼀一个⽇日期期间结束 this.publish('selectRangeEnd', { defaultFn: this._defSelectRangeEndFn }); ! //错误事件 this.publish('error', { defaultFn: this._defErrorFn });
preventDefault function EventFacade(payload) { this.prevented =
false; merge(this, payload); } ! EventFacade.prototype.preventDefault = function() { this.prevented = true; };
preventDefault function CustomEvent(cfg) { this._type =
cfg.type; this._defaultFn = cfg.defaultFn; this._cancelable = cfg.cancelable; this._subscribers = []; } ! CustomEvent.prototype.fire = function(type, payload) { var e = new EventFacade(payload); this._subscribers.forEach(function(subscriber) { subscriber.fn.call(context, e); }); if (!e.prevented || !this._cancelable) { this._defaultFn && this._defaultFn.call(this, e); } };
preventDefault var component = new EventTarget(cfg); ! component.publish('show', {
cancelable: true, defaultFn: function() { component.container.css('display', 'block'); } }); ! component.on('show', function(e) { e.preventDefault(); }); ! component.fire('show', {}); 不会被执⾏行
preventDefault component.fire(“show"); execute subscribers defaultFn prevented? end yes no
bubbles? <ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> <li>five</li> <li>six</li> We don’t
have a DOM-like structure
bubbles ManagerClass instance1 instance2 instance3 instance4 instance5 instance6 manager.on('select', doSomething);
bubbles function EventTarget() { this._customEvents =
{}; this._targets = []; } ! EventTarget.prototype.addTarget = function(target) { this._targets.push(target); }; ! EventTarget.prototype.removeTarget = function(target) { this._targets.splice(this._targets.indexOf(target), 1); };
function CustomEvent(cfg) { this._type = cfg.type;
this._defaultFn = cfg.defaultFn; this._cancelable = cfg.cancelable; this._bubbles = cfg.bubbles; this._subscribers = []; } ! CustomEvent.prototype.fire = function(type, payload) { var e = new EventFacade(payload); e.currentTarget = e.target = this; this.fireEvents(e); this.fireDefautFn(e); }; bubbles
bubbles CustomEvent.prototype.fireEvent = function(e) { this._subscribers.forEach(function(subscriber)
{ subscriber.fn.call(context, e); }); if (this._bubbles) { this._targets.forEach(function(target) { e.currentTarget = target; target.fireEvent(e); }); } }; ! CustomEvent.prototype.fireDefautFn = function(e) { if (!e.prevented || !this._cancelable) { this._defaultFn && this._defaultFn.call(this, e); if (this._bubbles) { this._targets.forEach(function(target) { target.fireDefautFn(e); }); } } };
FilterPanel:select Radio:select CheckBox:select Cascade:select
bubbles radio.addTarget(filterPanel); checkBox.addTarget(filterPanel); cascade.addTarget(filterPanel); ! filterPanel.on('select', doSomething);
! radio.fire('select'); checkBox.fire('select'); cascade.fire('select'); 均冒泡⾄至filterPanel
delegate? ManagerClass instance1 instance2 instance3 instance4 instance5 instance6 manager.delegate('select', 'instance',
doSomething);
delegate function EventTarget(prefix) { this._customEvents =
{}; this._targets = []; this._prefix = prefix; }
delegate radio.addTarget(filterPanel); checkBox.addTarget(filterPanel); cascade.addTarget(filterPanel); ! filterPanel.delegate(‘select', 'radio',
doSomething); ! radio.fire('select'); checkBox.fire('select'); cascade.fire('select'); 只有radio冒泡⾄至filterPanel
stop function EventFacade(payload) { this.prevented =
false; this.stopped = 0; merge(this, payload); } ! EventFacade.prototype.stopPropagation = function() { this.stopped = this.stopped == 0 ? 1 : this.stopped; }; ! EventFacade.prototype.stopImmediatePropagation = function() { this.stopped = 2; };
CustomEvent.prototype.fireEvent = function(e) { this._subscribers.forEach(function(subscriber) {
(e.stopped != 2) && subscriber.fn.call(context, e); }); if (this._bubbles && !e.stopped) { this._targets.forEach(function(target) { if (e.stopped != 2) { e.currentTarget = target; target.fireEvent(e); } }); } }; ! CustomEvent.prototype.fireDefautFn = function(e) { if (!e.prevented || !this._cancelable) { this._defaultFn && this._defaultFn.call(this, e); if (this._bubbles && !e.stopped) { this._targets.forEach(function(target) { target.fireDefautFn(e); }); } } };
stop radio.addTarget(filterPanel); ! filterPanel.on('select', doSomething); radio.on('select', doOtherthing);
radio.on('select', function(e) { //e.stopPropagation(); e.stopImmediatePropagation(); }); ! radio.fire('select'); ⽴立刻停⽌止 停⽌止⽗父组件
defaultTargetOnly function CustomEvent(cfg) { this._type =
cfg.type; this._defaultFn = cfg.defaultFn; this._cancelable = cfg.cancelable; this._bubbles = cfg.bubbles; this._defaultTargetOnly = cfg.defaultTargetOnly; this._subscribers = []; } ! CustomEvent.prototype.fireDefautFn = function(e) { if (!e.prevented || !this._cancelable) { this._defaultFn && this._defaultFn.call(this, e); if (this._bubbles && !e.stopped && !this._defaultTargetOnly) { this._targets.forEach(function(target) { target.fireDefautFn(e); }); } } };
defaultTargetOnly radio.publish({ bubbles: true,
defaultTargetOnly: true, defaultFn: radio._render }); ! filterPanel.publish({ defaultFn: filterPanel._render }); ! radio.render = function() { this.fire('render'); }; ! radio.addTarget(filterPanel); filterPanel.on('render', doSomething); ! radio.render(); 触发filterPanel的render事件 不会触发
# More Than DOM
after moment? ! var component = new EventTarget(cfg); !
component.publish('show', { defaultFn: function() { component.container.css('display', 'block'); } }); ! component.on('show', doSomething); ! component.fire('show'); 如果想在show完之后处理其他逻辑怎么办? component.after('show', doSomething); we need this!!!
after function CustomEvent(cfg) { this._type =
cfg.type; this._defaultFn = cfg.defaultFn; this._cancelable = cfg.cancelable; this._bubbles = cfg.bubbles; this._defaultTargetOnly = cfg.defaultTargetOnly; this._subscribers = []; this._afterSubscribers = []; }
CustomEvent.prototype.fire = function(type, payload) { var
e = new EventFacade(payload); e.currentTarget = e.target = this; this.fireEvents(e); this.fireDefautFn(e); this.fireEvents(e, 'after'); }; ! CustomEvent.prototype.fireEvent = function(e, when) { var subs = when == 'after' ? this._afterSubscribers : this._subscribers; if (when == 'after' && e.prevented) { return; } this._subscribers.forEach(function(subscriber) { (e.stopped != 2) && subscriber.fn.call(context, e); }); if (this._bubbles && !e.stopped) { this._targets.forEach(function(target) { if (e.stopped != 2) { e.currentTarget = target; target.fireEvent(e, when); } }); } };
instance event _defEventFn instance.on(event,handler) instance.after(event,handler) after
stateChange this.after('activeItemChange', this._afterActiveItemChange); this.after('resultsChange', this._afterResultsChange); this.after('visibleChange', this._afterVisibleChange); autocomplete/list.js
instance event _defEventFn instance.on(event,handler) instance.after(event,handler) stateChanged beforeStateChange change state
Features ✓ add/remove/fire ✓ default/bubbles ✓ stop/prevent ✓ delegate ✓
after
component component component component Events your app manager
Rethink how you use events
Rethink how you build your apps
None
# Q&A
further reading • http://www.nczonline.net/blog/2010/03/09/custom-events-in-javascript/ • https://developer.mozilla.org/en-US/docs/Web/API/document.createEvent • http://yuilibrary.com/yui/docs/event-custom/ • http://msdn.microsoft.com/en-us/magazine/hh201955.aspx
• http://otaqui.com/blog/1374/event-emitter-pub-sub-or-deferred-promises-which- should-you-choose/ • http://coding.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/ • http://flightjs.github.io/ • http://www.youtube.com/watch?v=s_7VjN3qxe8