YUI3学习(五)--- Event
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'])注册事件监听;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'。使用事件分类的好处是便于根据类别管理事件,如接下来介绍的移除事件监听。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)模拟触发事件。//基于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的事件注册和原始的事件注册都可以被触发。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属性。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,153,如果同时监听多个修饰键(shift、ctrl、alt),使用+号分割。一旦指定了多个修饰键,仅当所有修饰键都按下才会触发。如 press:65,66+shift+ctrl ?就意味着在shift和ctrl键同时按下,且按下65或66键码中的一个的时候,才会触发。<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"); });?<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");});?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}); //或使用如下方式绑定上下文和参数。事件额外配置信息为nullmyNode.on("flick", o.flickHandler, null, o, arg1);Y.on('flick',o.flickHandler,'#myNode',null,o,arg1);最后注意:Flick gesture event目前不能用于delegate。?// 当鼠标或手指按下时触发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。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 还提供了一个合成事件的示例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 APIvar publisher = new Publisher();publisher.on("publisher:testEvent", function(e) { //处理事件代码});?Firing the Event在合适的时候,触发自定义事件。代码如下:publisher.fire("publisher:testEvent");?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) { // 不会执行 });});});??