YUI Event 组件 通过提供简单的Dom事件响应接口为开发事件驱动的应用提供了便利。该组件同时也包含自定义事件支持,自定义事件允许你在程序中发布事件,其他的组件可以订阅这些事件做出响应。
?以下分步介绍YUI事件系统的相关功能。
基本DOM事件
Y.on()注册事件
使用基本Event组件的方法,需要引入'event'模块;在Node模块介绍中提到过如何为Node注册Dom事件的方法。
YUI().use('node',function(Y){
Y.on('click',function(e){
alert('id==foo's dom is clicked');
},'#foo');
});
?(注:查看node模块源码,node依赖'event-base', 'event-delegate'模块,所以引入node不需要再引入这两个模块;当然,引入也是不影响效率的。)
上面的例子为id等于foo的元素注册了一个click事件处理器;如果selector改为多匹配,也就为一组元素同时注册了同一个事件处理器。
此外,还可以为一个或多个dom节点(非YUI Node,如document.getElementById获取)和selector数组(['#foo','#foo div'])注册事件监听;
Y.on() 为事件处理函数绑定上下文及参数
Y.on可以为注册的事件处理函数绑定上下文和参数,可以灵活的设置函数的执行上下文环境;
YUI().use( "node-base", function(Y) {
var contextObj = {
name: "context"
};
// 事件处理函数
function handleClick(e, arg1, arg2) {
alert(this.name); //'context'
alert(arg1+','+arg2);// "argumentOne", "argumentTwo"
}
var handler = Y.on("click", handleClick, "#list .odd", contextObj, "argumentOne", "argumentTwo");
});
catalog事件分类
在事件类型字符串前面添加类型前缀实现事件分类,例:'menu|click'。
使用事件分类的好处是便于根据类别管理事件,如接下来介绍的移除事件监听。
Y.detach移除事件监听
移除事件监听需要使用detach方法。示例如下:
YUI().use('node-base', function(Y) {
function handleClick(e) {
Y.log(e);
}
//事件处理器句柄
var handler = Y.on("menu|click", handleClick, "#foo");
//利用事件处理器句柄对象移除事件监听
handler.detach();
//or
Y.detach(handler);
//利用事件分类移除事件监听;未分类事件不能采用;
Y.detach('menu|click');
Y.detach('menu|*');
//利用事件处理函数移除事件监听;
Y.detach("click", handleClick, "#foo");
});
此外:Event还提供了针对单个元素的事件清除方法
//移除#input1上所有事件监听
Y.Event.purgeElement('#input1')
//移除#input1及所有子节点上所有事件监听
Y.Event.purgeElement('#input1',true);
//仅移除#input1上click事件监听
Y.Event.purgeElement('#input1',false,'click');
?模拟事件
模拟事件是基于浏览器自身创建的事件,在绝大的数情况下与用户交互发起的事件表现一致。创建的事件对象包含事件相关的数据并同样支持事件冒泡。(有时候这些事件对象属性是特定于浏览器的,所以,不管是普通事件还是模拟事件,YUI推荐使用Y.Event的跨浏览器相关方法/属性来获取相关事件属性值,如target、relatedTarget、currentTarget及charCode等)。
每个事件目标在事件的整个生命周期会同步执行所有的事件处理程序。
使用模拟事件,需要引入'?node-event-simulate ' 模块 。这样所有Node实例都可以调用simulate方法模拟事件,也可以使用Y.Event.simulate(target,type,options)模拟触发事件。
模拟鼠标事件
可以模拟的鼠标事件有以下几个:
click\dblclick\mousedown\mouseup\mouseover\mouseout\mousemove
基本上包含了所有的鼠标事件。示例代码如下:
//基于YUI 的事件注册
Y.on('click',function(e){
alert('e');
},'#input1');
//原始的事件注册(非ie)
document.getElementById('input1').addEventListener('click',function(e){
alert('ee')
});
//模拟触发。以上两个都可以被触发
Y.one('#input1').simulate('click');
???注:基于YUI的事件注册和原始的事件注册都可以被触发。
此外,YUI3还支持模拟事件的同时,指定附加信息,比如是否按下shift键等。
这些额外的附加信息取决于模拟的事件,以下是附加信息限制列表:
1.detail 指定按钮被点击的次数(限于DOM兼容浏览器)
2.screenX/screenY 相对于整个屏幕的鼠标事件坐标
3.clientX/clientY 相对于浏览器客户端的鼠标事件坐标
4.ctrlKey/altKey/shiftKey/metaKey ctrl、alt、shift、metaKey各自的状态(按下是真,弹起是假)
5.button 点击事件所使用的鼠标按键(0代表左键,1代表右键,2代表中键)
6.relatedTarget 鼠标移动时指向的目标
2.screenX/screenY 相对于整个屏幕的鼠标事件坐标
3.clientX/clientY 相对于浏览器客户端的鼠标事件坐标
4.ctrlKey/altKey/shiftKey/metaKey ctrl、alt、shift、metaKey各自的状态(按下是真,弹起是假)
5.button 点击事件所使用的鼠标按键(0代表左键,1代表右键,2代表中键)
6.relatedTarget 鼠标移动时指向的目标
模拟事件附加信息示例
Y.on('click',function(e){
alert('e')
},'#input1');
Y.one('#input1').simulate('click',{ altKey: true});
??模拟键盘事件
以下三种键盘事件可以模拟:keyup、keydown、keypress
与模拟鼠标事件一样,通过simulate方法模拟键盘事件。
对于keyup和keydown事件,需要明确设置附加信息‘keyCode’;对于keypress事件则需要明确设置‘charCode’。
在很多情况下,keyCode和charCode值是一样的。示例如下:
YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myDiv");
//simulate a keydown on the A key
node.simulate("keydown", { keyCode: 97 });
//simulate a keyup on the A key
node.simulate("keyup", { keyCode: 97 });
//simulate typing "a"
node.simulate("keypress", { charCode: 97 });
});
?模拟键盘事件同时支持ctrlKey、altKey、shiftKey和metaKey属性。
?注意:因为浏览器实现的差异,按键事件在所有浏览器中的模拟表现不尽相同。例如,模拟textbox的keypress事件,只有firefox支持按键点击的时候对textbox的更新。而对于其他的浏览器,事件已注册,而且所有的事件处理程序也被调用,然而textbox的显示和value属性却没有更新。这些不同的差异在未来有可能因为浏览器的支持而得到改善。
UI事件
可模拟的UI事件有以下几个:blur、focus、change、select、scroll、resize
模拟UI事件同样使用simulate方法,不同的是不需要设置设置任何的额外信息。
UI事件一般用于表单元素如input select等。
页面元素加载周期相关事件
available、contentready、domready
元素的available事件当元素刚刚被检测到的时候触发,即目标元素可以响应getElementById方法的时候。
Y.on('available',func,selector,context);
contentready事件与available方法的使用一样,区别是contentready是在目标元素和它的下一个元素可以响应getElementById方法的时候触发,这样保证目标元素的内容全部加载完成。
domready事件将在DOM加载完毕后触发。只有DOM所有的结构加载完毕才被视为可用,特别是在IE中,如果在DOM加载完成前使用脚本向DOM中插入信息就可能导致浏览器崩溃。
更有意义的是,在页面图片载入完毕前,DOM就已经加载完毕。所以domready相对于window.onload,是一个更好的选择去执行页面载入后的脚本。
Y.on('domready',function(){});
Key事件
key事件,允许你定义一个当指定的一个键位或一组键位按键时执行的函数。
key事件依赖于模块‘event-key’,key事件不是普通的按键监听事件,提供了更为高级的按键监听功能。
示例:id=text1元素关联了一个可以事件监听器,定义当按下回车键(keycode=13)时触发,并且执行后自动取消监听以保证只执行一次。
YUI().use('event-key', function(Y) {
// 保存Y.on的返回值,用于一会移除监听器
var handle = Y.on('key', function(e, arg1, arg2, etc) {
Y.log(e.type + ": " + e.keyCode + ' -- ' + arg1);
// 停止冒泡并阻止默认事件
e.halt();//e.preventDefault();e.stopPropagation();
// 取消监听,保证只会执行一次
handle.detach();
}, '#text1', 'down:13', Y, "arg1", "arg2", "etc");
//此外,让事件监听器执行一次还可以使用Y.once。
});
?key事件按键监听规则
?在上例中,‘down:13’ 即是其中一项规则,规定了只有按下回车键时触发。规则如下:
1,按键事件类型(up,down,press),后加一个冒号
2,如果要监听多个按键,使用逗号分割。一旦指定了多个按键,检测到任何一个都会触发事件监听器。如 down:13,14,15
3,如果同时监听多个修饰键(shift、ctrl、alt),使用+号分割。一旦指定了多个修饰键,仅当所有修饰键都按下才会触发。如 press:65,66+shift+ctrl ?就意味着在shift和ctrl键同时按下,且按下65或66键码中的一个的时候,才会触发。
委托事件
?? ?委托delegate是一种 ?在父元素上定义单个事件处理程序从而监听一组子元素事件的技术。主要是依赖事件的冒泡机制。这对于降低页面上消耗资源的事件处理程序可能是一个可靠并且有效的缓解策略。(更多委托事件请参考这里)
?? ?YUI3的委托事件使用很简单,引入模块‘event-delegate’即可使用Y.delegate(type,func,container,selector)创建委托事件。
以下示例创建一个委托事件监听器。
<div id="container">
<ul>
<li id="item-1"><em>Item Type One</em></li>
<li id="item-2"><em>Item Type Two</em></li>
<li id="item-3"><em>Item Type Three</em></li>
</ul>
</div>
在容器‘container’上面绑定委托事件监听器,在点击了li子节点时会触发事件。
YUI().use("event-delegate", function(Y) {
Y.delegate("click", function(e) {
// 通过'this'获取选择器提供的列表元素
Y.log("Default scope: " + this.get("id"));
// 也可以通过e.currentTarget来获取选择器提供的列表元素
Y.log("Clicked list item: " + e.currentTarget.get("id"));
// 实际点击的目标,可能是匹配的元素也可能是它的后代元素。
Y.log("Event target: " + e.target);
// 通过e.container获取容器元素
Y.log("Delegation container: " + e.container.get("id"));
}, "#container", "li");
});
?? 注:这里提到了监听函数上下文对象this及表示事件的对象e的属性target、currentTarget及container,在后面会详细比较这几个对象的意义。
?同样,也可以直接在容器对象上面建立委托事件。
var container = Y.one("#container");
container.delegate("click", function (e) {
// Same as above
}, "li");
?focus/blur事件
YUI3增强的focus和blur事件支持冒泡,DOM的focus和blur事件不支持冒泡,因此可以在支持focus和blur的元素上使用YUI的focus/blur事件作为替代。类似于delegate,在元素上建立单一事件监听器,从而达到监听所有子元素focus/blur事件。减少监听器数量已经被证明是提高性能的策略。
使用YUI focus/blur事件,需要引入模块‘event-focus’
以下示例监听toolbar下面所有子节点的focus事件,只需要建立一个监听就可以实现。
<div id="toolbar">
<input type="button" id="button-cut" name="button-cut" value="Cut">
<input type="button" id="button-copy" name="button-copy" value="Copy">
<input type="button" id="button-paste" name="button-paste" value="Paste">
</div>
?YUI().use("event-focus", function(Y) {
var handle = Y.on("focus", function(e, arg1, arg2, etc) {
Y.log("target: " + e.target + ", arguments: " + arg1 + ", " + arg2 + ", " + etc);
// Attach to the element with the id of "toolbar", make Y the context, add arguments
}, "#toolbar", Y, "arg1", "arg2", "etc");
});
?mouseenter/mouseleave事件
受IE支持mouseenter和mouseleave事件的影响,YUI为所有的A级浏览器提供了该组事件。
与mouseover和mouseout相比,由于mouseenter和mouseleave不支持冒泡,所以可以提高性能,对DOM的改变频率也会降低。
依赖模块‘event-mouseenter’
示例代码
<div id="container">
<ul>
<li><em>Item Type One</em></li>
<li><em>Item Type Two</em></li>
<li><em>Item Type Three</em></li>
</ul>
</div>
?由于mouseenter/mouseleave 不支持事件冒泡,所以进入container后在子节点中移动不会再触发事件。 YUI().use("event-mouseenter", function(Y) {
Y.on("mouseenter", function (e) {
Y.log("Mouse entered: " + this.get("id"));
}, "#container");
Y.on("mouseleave", function (e) {
Y.log("Mouse left: " + this.get("id"));
}, "#container");
});
?hover事件
在YUI3.3.0版本中增加了hover事件支持。hover事件是基于mouseenter和mouseleave事件的合成事件(下面会介绍)。
依赖模块‘event-hover’。与其他使用不同的是,需要传入两个事件处理函数。
如下示例:
YUI().use("event-hover", function(Y) {
function over(e) {
this.addClass("hover");
}
function out(e) {
this.removeClass("hover");
}
var button = Y.one("#myButton");
// 订阅独立的事件。只能在Node对象上这样使用
button.on({
mouseenter: over,
mouseleave: out
});
// 或者通过hover事件来订阅
button.on("hover", over, out);
// 同样也可以通过委托来订阅
Y.one("#menu").delegate("hover", over, out, ".menuItem");
});
?Touch事件
YUI DOM事件延伸到对触摸事件的支持。引入‘event-touch’模块,就可以在你的程序中使用touch事件。
以下常见的低级别的touch事件支持大多数启用触摸的设备。
touchstart \ touchmove \ touchend \ touchcancel
此外,在IOS系统中也支持gesturestart,gesturechange和gestureend,由于其他的触摸OS缺乏DOM级别的多点触摸,所以YUI不能支持这些事件。
(对大部分开发者来说使用较少,略去)
手势支持
?
event-gestures模块提供了一套跨越触摸和鼠标输入设备的合成事件描述常见的用户交互手势。这些手势事件可以被用在开发需要运行在触摸和鼠标输入设备上的程序。特别是在拖放(DD)组件上面。
在手势层上面构建拖放支持,YUI将在触摸和鼠标设备上支持拖放的全部功能。(特别是下面要讨论的gesture move 事件)
DD只需要为手势事件编写代码,无论是发生开始、移动和结束的触摸事件还是鼠标事件,拖放操作会在设备表现出一致的效果。
event-gestures 模块目前有两个子模块,’event-flick' 'event-move',更多跨设备的手势事件会在将来的版本中提供。
Flick Gesture 轻弹手势事件
依赖模块'event-flick';当用户开始一个轻弹手势(使用手指或鼠标)的时候会触发Flick Gesture事件,你可以像监听DOM事件一样监听flick事件。
示例:
myNode.on("flick", function(e) {
// 可以获取flick事件的一些特别的属性
var flick = e.flick,
velocity = flick.velocity,
distance = flick.distance,
axis = flick.axis,
startX = flick.start.pageX,
startY = flick.start.pageY,
// 以下表示mouseup或touchend时的事件属性
endX = e.pageX,
endY = e.pageY,
endTarget = e.target;
});
?此外,在订阅flick事件时,可以传入额外的配置信息控制事件的触发。
?
//回调函数未设置上下文环境和参数
myNode.on("flick", flickHandler, {
//只有在轻弹操作距离不小于20px、速度不小于0.8px/ms时才触发
minDistance:20,
minVelocity:0.8,
//阻止默认事件
preventDefault:true
});
//回调函数绑定上下文环境和参数
myNode.on("flick", Y.bind(o.flickHandler, o, arg1), {
minDistance:20,
minVelocity:0.8,
preventDefault:true
});
//或使用如下方式绑定上下文和参数。事件额外配置信息为null
myNode.on("flick", o.flickHandler, null, o, arg1);
Y.on('flick',o.flickHandler,'#myNode',null,o,arg1);
最后注意:Flick gesture event目前不能用于delegate。?Move Gesture 移动手势事件
Flick是高层次手势,它封装了互动的开始和结束信息。而gesturemovestart, gesturemove 和 gesturemoveend事件是低层次的手势事件,用来识别动作的开始、过程和结束。它们可以被用来组建更高层次的手势事件。例如刷卡手势。
为了使用Move手势事件,需要引入'event-move‘模块,该模块提供了上面三个低层次事件的支持。
// 当鼠标或手指按下时触发
myNode.on("gesturemovestart", function(e) {...}, {
//在手势移动了最少3px时延时1s执行
minDistance: 3,
minTime:1000,
//如果目标对象不是select标签,阻止默认行为的触发
preventDefault:function(e) {
return (e.target.get("tagName").toLowerCase() !== "select");
}
});
// 当鼠标或手指按下移动时触发
//节点需要注册gesturemovestart才有效。
myNode.on("gesturemove", function(e) {...});
//当鼠标或手指移动结束后触发
//节点需要注册gesturemovestart才有效
myNode.on("gesturemoveend", function(e) {...});
//鼠标或手指在文档中移动时触发
//不需要document注册gesturemovestart (standAlone:true)
Y.one("document").on("gesturemove", function(e) {...}, {
standAlone:true
});
?关于move手势三个事件注册的完整参数配置可以参考 gesture move API 文档注:move gesture event 可以用于delegate。
事件关联
值得一提的是,以上三种类型的手势事件与Flick手势事件不同,因为它们是相互关联的(它们是同一个手势事件的开始、过程和结束)。
当使用move手势编写程序时,如果没有接收到‘start’手势事件就不会触发‘move’事件;如果没有接收到‘start’和‘move’事件就不会触发‘end’事件。这是绑定到同一节点上的move手势事件的默认行为。
但是,如果你想在不监听gesturemovestart事件的前提下就可以监听gesturemove或gesturemoveend事件,你可以使用 standAlone:true 配置。
实际上,监听mousemove/touchmove和mouseup/touchend的监听器默认是建立在document上的,节点只是提供了关联事件的共享上下文对象。
Synthetic?DOM Event (合成事件)
YUI事件系统支持在浏览器原生DOM事件基础上为节点定义新的事件。类似下面介绍的自定义事件,允许你标记兴趣点。但与自定义事件不同,合成事件只关系所在的页面元素而不是程序中的类对象。在本质上,它们增加了可用的dom事件列表,你可以像订阅普通的DOM事件一样给dom节点订阅这些合成事件。该功能依赖’event-synthetic'模块
创建合成事件使用Y.Event.define()方法。示例如下:
YUI().use("event-synthetic", function(Y) {
// 创建名称为 "clickoutside"的合成事件
Y.Event.define("clickoutside", {
// 当程序订阅'clickoutside‘事件时触发
on: function (node, subscription, notifier) {
function outside(clickTarget) {
return clickTarget !== node && !clickTarget.ancestor(
function (parent) {
return parent === node;
});
}
var handle = Y.one('doc').on('click', function (e) {//触发合成事件的事件
if (outside(e.target)) {
notifier.fire(e);
}
});
subscription.clickHandle = handle;
},
//撤销事件
detach: function (node, subscription, notifier) {
subscription.clickHandle.detach();
},
//当程序使用委托订阅合成事件时触发
delegate: function (node, subscription, notifier, filter) {
// ...
},
//撤销委托事件
detachDelegate: function (node, subscription, notifier, filter) {
//...
}
});
Y.on("clickoutside", function (e) {
this.addClass('hidden');
}, "#menu");
??关于合成事件的高级配置,可参考YUI api 文档?此外,YUI examples 还提供了一个合成事件的示例
Custom Events(自定义事件)
YUI的自定义事件系统允许你定义和使用超越DOM事件的事件,这些事件可监听类的状态。自定义事件很想DOM事件,可以冒泡,传递事件对象,有自己的传播机制和抑制默认行为机制等等。本届介绍几种常用的YUI自定义事件并提供示例代码。自定义事件依赖模块 'event-custom' 。
Simple Custom Events Using Y.on
使用Y.on订阅一个自定义事件,通过调用Y.fire方法可以在程序中触发自定义事件。
YUI().use('event-custom', function(Y) {
Y.on('customapp:started', function(arg1, arg2, arg3) {
Y.log('Custom App Started, now I can do a few things);
// the arguments 1, 2, and 3 were provided by fire()
});
Y.fire('customapp:started', 1, 2, 3);
});
Defining a Custom Event on an Event Target
通用的创建自定义事件的方法是用EventTarget对象去augment(扩充)一个对象,使之具备发布订阅和触发自定义事件的能力,同时也可以作为事件冒泡中的一环。
?发布一个自定义事件示例
YUI().use('event-custom', function(Y) {
function Publisher() {
//创建一个自定义事件publisher:testEvent
//如果不需要覆盖默认的发布事件配置参数,可以不用显示的声明
this.publish("publisher:testEvent", {
// 覆盖默认的配置
});
}
// 使用EventTarget扩充Publisher
Y.augment(Publisher, Y.EventTarget, null, null, {
//该参数为EventTarget构造函数设置参数,修改事件默认属性
});
// 如果使用默认事件属性,使用如下方式即可
// Y.augment(Publisher, Y.EventTarget);
});
? 事件配置参数可参考publish API
Subscribing (Listening) to a Custom Event
使用on 方法 订阅(监听)自定义事件,如下代码订阅上面发布的自定义事件
var publisher = new Publisher();
publisher.on("publisher:testEvent", function(e) {
//处理事件代码
});
?Firing the Event
在合适的时候,触发自定义事件。代码如下:
publisher.fire("publisher:testEvent");
?跨YUI实例广播事件/共享信息
可以在发布一个自定义事件的时候设置事件的广播(broadcast)属性,使它可以被任意的代码监听到。
可以限制事件只能通知当前YUI实例内的对象,也可以将事件全局广播使整个页面的对象都可以使用这个事件。
下面示例是跨不同YUI实例(安全沙箱)共享事件信息的主要方法
YUI().use('event-custom', function(Y) {
var publisher = new Y.EventTarget();
publisher.name = 'broadcast publisher';
publisher.publish('instance_notification:foo', {
broadcast: 1, // 在当前YUI实例下有效
emitFacade: true // 是否创建EventFacade事件对象
});
// 在当前YUI安全沙箱内有效
Y.on('instance_notification:foo', function(e) {
Y.log(e.target.name); // broadcast publisher //emitFacade=true
});
var publisher2 = new Y.EventTarget();
publisher2.name = 'global publisher';
publisher.publish('global_notification:foo', {
broadcast: 2, // 全局有效
emitFacade: true
});
//创建一个新的YUI沙箱
YUI().use('event-custom', function(Y2) {
//在新的沙箱内,全局事件有效
Y2.Global.on('global_notification:foo', function() {
Y.log(e.target.name); // global publisher
});
//无效
Y2.on('instance_notification:foo', function(e) {
// 不会执行
});
});
});
?EventFacade相关属性
EventFacade包装原始的event对象,在DOM事件及自定义事件中作为第一个参数传递给回调函数。
关于DOM事件的event属性可以参考w3c相关讲解?
本节主要说明EventFacade的几个常用属性和方法:
Y.on('click',function(e){
this; //注册事件的对象?;如果是委托事件,this?不会代表委托对象,而只表示被委托的单个对象。
??//如果是NodeList注册,返回NodeList集合对象;?
e.target; ? ? ? ? ?//触发事件的对象(在click事件中即被点击的对象,并不一定是注册的对象)
e.currentTarget ; ? //?注册事件的对象;如果是委托事件,也只表示被委托的单个对象。
?//?与this不同,如果是NodeList注册,返回集合中被点击的对象 ?
e.container; ? ? ? ? ?//?委托对象 ,非委托事件 返回 undefined
??
e.stopPropagation(); //停止事件冒泡。
e.preventDefault(); ?//阻止事件默认行为。
e.halt(); //相当于调用stopPropagation() 和 preventDefault()
}
?
?
?