Event Handling
Enyo采用了消息传递策略来间接地在不同组件之间通信。我们把这些信息作为事件,这与常见的DOM用法一直。一般事件按照child-parent顺序在组件树中冒泡。在使用dom包时,DOM事件和自定义事件是一致的。
使用事件的关键在于component组件设计的封装。大部分时候,一个component的children不应当知道他们的父类。所有child发出的事件应当由父类决定是否处理而不应在child中调用父类的方法处理事件。
虽然由child向parent发送事件是enyo的标准,但在一些情况下这种实现模式效率低下且代码笨拙。因此enyo还提供了另一种通信方法---enyo.Signals ,文章后面会提到该方法。
Sending Events
Component组件使用events语句块来声明它发送的事件:
events: {
onStateChanged:""
}
注意,为了方便 事件的名称总是以“on”开头。
对于每一个在component组件的event语句块中注册过的事件,都会在该kind上创建一个helper函数do<EventName>,component组件可以调用该方法来发送事件到component组件树。这个函数接受一个可选的inEvent参数,该参数可以包含特定的事件信息并传递给处理函数。例如要从上面的例子中发送“onStateChanged”事件,一个组件要调用
this.doStateChanged(newState) // parameter is specific to the "onStateChanged" event
在内部,do<EventName>函数包装了enyo原始的发送事件组件树的冒泡函数。
this.bubble(inEventName <, inEvent, inSender>)
inEventName是包含on前缀的事件名称
inEvent是可选的对象,它包含特定的事件信息,必须是js对象不能使原始的对象。
inSender 该参数应该总是省略掉,尽管你可以用它来为接下来的处理函数强制指定一个特定的sender。
注意:声明一个event块使用do<EventName>helper函数能够更好的直接调用冒泡函数,event语句块有更好的描述性并且更好的定义了kind的接口。
Creating Handlers
事件的处理程序是捕捉children冒泡事件的函数。例如:
1 myEventHandler: function(inSender, inEvent) { 2 // Can return true to indicate that this event was handled and 3 // propagation should stop 4 }
这个处理函数可以返回一个为真的结果来阻止event事件的传播。否则,它会继续从component组件树中传播。
注意,返回真值的方法与传统的DOM协定不同(返回值将决定默认的action是否会发生)。如果你需要控制DOM事件的默认action,使用现代版的inEvent.preventDefault()。inEvent.preventDefault()不会阻止enyo事件的冒泡,从处理程序返回true能阻止冒泡。
因为在停止前事件一直冒泡,事件的sender可能与原始的sender不同。事件的处理程序可以用inEvent.originator来获取原始的组件。例如,点击一个按钮,按钮发出一个onclick事件,沿control链冒泡,经由父类冒泡到祖父类。从祖父类来看,事件发起源是button事件的sender是button的父类。
Attaching Handlers to Events
在组件中有两种通用的处理事件的方式。第一种:
components: [
{name: "thing", ontap: "thingTap"}
],
thingTap: function(inSender, inEvent) {
// do stuff
}
第二种:
handlers: {
ontap: "anythingTap"
},
anythingTap: function(inSender, inEvent) {
// do stuff
}
如果你同时使用这两种事件处理策略,你会在两处都接收到event。你可以在tingTap中阻止冒泡来避免这一情况。例如:
components: [
{name: "thing", ontap: "thingTap"}
],
handlers: {
ontap: "anythingTap"
},
thingTap: function(inSender, inEvent) {
// taps on _thing_ will bubble up to _anythingTap_ also,
// unless I stop propagation here
return true; // handled here, don't propagate
}
anythingTap: function(inSender, inEvent) {
// do stuff
}
如果你需要处理更复杂的程序,可以使用inSender和inEvent.originator属性来帮助辨别事件的出处。
DOM (and DOM-like) Events
在enyo中,DOM事件可以一直冒泡到document被enyo.dispatcher处理。Dispatcher决定事件发送到哪里并为不同的事件处理程序提供插件接口。
只要有可能,dispatcher就会避免原始的DOM事件。为了强制阻止DOM事件冒泡,你可以从事件的处理方法中返回true。
除了target属性外,enyo指定了dispatchTarget属性来设置包含event target的enyo control。
下面的DOM事件由enyo处理:
- mousedown
- mouseup
- mouseover
- mouseout
- mousemove
- click
- dblclick
- change
- input
- keydown
- keyup
- keypress
- resize
- load
- unload
- message
如果你希望enyo处理额外的DOM事件(如mousewheel),这样做:
document.onmousewheel = enyo.dispatch;
除了DOM事件,还有一些常规的输入事件,dispatcher会把它们作为类DOM事件发送(如ontap, ondown, onup, ondragstart, ondrag, ondragfinish, onenter, 和 onleave)。这些事件大部分都是跨平台的,所以客户端代码不必区分touch和mouse事件接口。
由于一些约定,DOM事件和类DOM事件在作为enyo事件时仍然保持小写,但是在enyo kind中声明的事件使用驼峰式大小写(如onStateChanged)。关于类DOM事件的更多信息请参考 User Input。
Signals
在你的应用中有时两个不相关的组件可能需要通信。如果使用标准的event模型,你将把事件传递给一个公共父类(最坏的情况是传递到app的顶级kind)然后将事件返回给目标组件。由于这样需要大量的plumbing,enyo提供了替代选项。
Enyo.Signals提供了绕过正常组件树来广播和订阅全局信息的方法。在enyo框架内部,DOM事件没有目标节点作为signal来传播。这些事件包括windows event,如onload和onbeforeunload,还有直接从document发出的事件如当document获得焦点时的onkeypress事件。Signal对于连接非enyo事件并使用application代码中的enyo kind处理这些事件也很有用。
为了广播信息,sender只要调用enyo.Signals的静态send函数即可:
enyo.Signals.send(inEventName, inEvent);
为了监听signal,组件需要在它的components语句块中包含Signals实例。需要通过设置Signals对象的messageName属性为"Signals"来指定处理方法。当收到messageName signal时调用该方法。例如:
1 enyo.kind({ 2 name: "Receiver", 3 components: [ 4 // 'onTransmission' is the message name and 'transmission' is the 5 // name of a handler method in my owner. 6 {kind: "Signals", onTransmission: "transmission"} 7 ], 8 transmission: function(inSender, inEvent) { 9 // respond to the signal 10 } 11 });
可以这样调用该函数来处理signal事件:
enyo.Signals.send("onTransmission");
注意,和所有的enyo信息处理方法一样,signal处理函数接收两个函数:一个发送信息的组件的引用和一个事件信息的包装类
一些需要注意的地方:
传递给send方法的signal名称必须配置接收Signals的message名称,都必须包含on前缀
所有注册了特定message的signals实例都会接收message
Send方法在enyo.Signals本身内部,不是Signals的实例。
不要滥用Signals。对象与全局通信耦合是非常糟糕的。