Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Events

 Events

How to build a custom event system

ningzbruc

March 19, 2014
Tweet

More Decks by ningzbruc

Other Decks in Programming

Transcript

  1. Simulate Events ! //  创建事件   var  event  =  button.createEvent('Event');

      ! //  初始化事件   event.initEvent('click',  true,  true);   ! //  触发事件   button.dispatchEvent(event);          
  2. 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事件
  3. How ! //  创建事件   var  event  =  button.createEvent('Event');  

    ! //  初始化事件   event.initEvent('whatever',  true,  true);   ! //  触发事件   button.dispatchEvent(event);          
  4. flight.js ! var  component  =  Component.attachTo('#container',  cfg);   ! component.on('select',

     doSomething);   ! component.trigger('select',  {          item:  'item'     });       模拟DOM事件
  5. 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);   };
  6. ! 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);          });   };
  7. function  Subscriber(fn,  context)  {          this.fn  =

     fn;          this.context  =  context;          //...  more  property   }
  8. ! var  component  =  new  EventTarget(cfg);   ! component.on('select',  doSomething);

      ! component.fire('select',  {          item:  'item'     });   ! component.detach('select',  doSomething);     触发select事件
  9. 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);   };
  10. default action ! var  component  =  new  EventTarget(cfg);   !

    component.publish('show',  {          defaultFn:  function()  {                  component.container.css('display',  'block');          }   });   ! component.on('show',  doSomething);   ! component.fire('show');      
  11. 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   });
  12. preventDefault function  EventFacade(payload)  {          this.prevented  =

     false;          merge(this,  payload);   }   ! EventFacade.prototype.preventDefault  =  function()  {          this.prevented  =  true;   };
  13. 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);          }   };
  14. 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',  {}); 不会被执⾏行
  15. 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);   };
  16. 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
  17. 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);                          });                  }          }   };
  18. bubbles radio.addTarget(filterPanel);   checkBox.addTarget(filterPanel);   cascade.addTarget(filterPanel);   ! filterPanel.on('select',  doSomething);

      ! radio.fire('select');   checkBox.fire('select');   cascade.fire('select'); 均冒泡⾄至filterPanel
  19. delegate function  EventTarget(prefix)  {          this._customEvents  =

     {};          this._targets  =  [];          this._prefix  =  prefix;   }  
  20. 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
  21. 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;   };
  22. 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);                          });                  }          }   };
  23. stop radio.addTarget(filterPanel);   ! filterPanel.on('select',  doSomething);   radio.on('select',  doOtherthing);  

    radio.on('select',  function(e)  {          //e.stopPropagation();          e.stopImmediatePropagation();   });   ! radio.fire('select'); ⽴立刻停⽌止 停⽌止⽗父组件
  24. 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);                          });                  }          }   };
  25. 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事件 不会触发
  26. 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!!!
  27. 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  =  [];   }
  28. 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);                          }                  });          }   };
  29. 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