YUI3:事件
YUI的事件功能为响应DOM事件提供一个简单的接口,让在浏览器中创建事件驱动的应用更容易了。YUI的事件功能包包含自定义事件对象(Custom Event object),自定义事件让你可以在代码中发布(publish)某一个瞬间或某一个事件,以便页面上其他的组件能订阅(subscribe)这些事件然后响应这些事件。
YUI的事件功能包有以下功能:
DOM事件处理
自动延迟解析(deferral)赋给不可用元素的处理器
自动调整函数执行环境(scope),可选择地指定函数执行环境
将浏览器差异规范化的event facade
自动清除DOM事件监听
易用的事件代理处理
在所有A级浏览器中的事件模拟
页面加载事件(元素层级的available和contentready以及DOM层级的domready)
键盘监听功能,响应特定的组合键
获得焦点/失去焦点事件抽象层,提供代理监听这些事件的能力
冒泡(bubbleable)、可取消(cancelable)的自定义事件,有内在的AOP特性
实现可定义的DOM事件,用以描述原生事件不提供的DOM的状态
Luke Smith – 事件进化
Luke Smith对YUI3的事件系统做深入的介绍,内容包括对DOM事件、事件代理、事件模拟、自定义事件的支持。
开始
引入相关文件
为YUI的事件功能引入源文件及相关文件最简单的办法是使用如下标签将YUI种子文件添加到你的页面中。YUI实例会自己加载所需文件。
当使用’event’模块时,YUI实例会自动加载YUI的事件功能的源文件以及相关文件。让YUI实例自动加载相关文件,可以避免手工管理页面组件所需的文件列表,还可以优化初始页面大小。
如果你想手工引入所需文件,可以使用YUI Dependency Configurator工具得到所需文件列表。
YUI实例
一旦页面上有YUI种子(yui-min.js)文件后,你可以创建一个新的YUI实例,通过use方法的第一个参数指定要加载的模块。
// Create new YUI instance, and populate it with the required modules
YUI().use('event', function(Y) {
// Event available, and ready for use.
});
传给use方法的最后一个参数是一个回调函数。当YUI实例加载完页面所需的文件后,该回调函数会被调用。一旦加载完那些文件后,实现’event’模块以及它依赖的模块的类会被提供给YUI实例。被赋予功能的YUI实例的引用会通过回调函数的参数传入回调函数中。然后,你就可以在你的回调函数中,基于你配置的YUI实例编写你的应用了。
关于YUI实例的创建和use方法的更多内容,请看YUI全局对象。
DOM事件
为DOM添加事件处理器很简单。定义事件处理器(函数),把事件处理器、事件名称引用、处理器绑定到的元素这三项传递给YUI的事件功能(Event Utility):
//处理该事件的函数
function handleClick(e) {
//将event facade传给log函数输出:
Y.log(e);
}
//假设我们在页面上有一个ID为"foo"的元素:
YUI().use('node-base', function(Y) {
Y.on("click", handleClick, "#foo");
});
代码解读:
1:创建一个YUI实例——YUI的事件功能依赖于’node-base’模块,所以YUI().use(“node-base”)会加载YUI的事件功能核心特性所需的文件。如果你加载’event-base’模块,也会得到一样的功能。一些特殊的事件实现被包装成子模块,不包含在’event-base’中。这些特殊的模块在下面会讲到,可以通过它们的名字加载他们。或者直接加载’event’模块,它包含所有的DOMYUI的事件功能。
2:定义一个回调函数来处理指定事件:handleClick(e)。
3:调用YUI实例的on方法,将事件绑定到DOM元素上。on方法要求三个参数:要绑定的事件(“click” 字符串),回调函数(handleClick),还有事件绑定到的元素(“#foo”,假定元素的ID为’foo’)。
因为我们通过ID来指定元素(‘#foo’ 字符串),而不是传入Node的引用或者是HTMLElement的引用,所以可以指向一个还未存在于页面上的元素。YUI的事件功能(Event Utility)通过ID查找元素,如果找不到,在页面加载完成15秒后,会再尝试地找一次。这个“自动异步”的功能让你在某些情况下直接在你的脚本中直接编写事件处理添加代码(event attachment code),而不是需要把这些事件处理添加代码写到一个页面加载完成才执行的函数中。
使用CSS选择器语法,以数组的形式传入多个选择字符串(selector string)、以数组的形式传入Node实例、以数组的形式传入DOM元素,为多个元素添加事件处理器。
YUI().use('node-base', function(Y) {
//可以通过选择器语法选择目标元素
Y.on("click", handleClick, "#foo p"); //把#foo元素下的所有的p节点作为目标
//elements can be targeted by Node references:
var foo = Y.one("#foo");
Y.on("click", handleClick, foo);
foo.on("click", handleClick); //同上
//可以传入元素的直接引用
var foo = document.getElementById("foo");
Y.on("click", handleClick, foo);
//在所有情况下,你可以传入一个数组
Y.on("click", handleClick, ["#foo p", "#bar"]);
});
使用on方法:控制执行环境和参数
使用Y.on,需要以下参数:
1. 事件名:指向响应的DOM事件的字符串(如:“click”或“mouseover”)。开发人员经常希望能有一个所有浏览器都支持的DOM事件完整列表。但是这样的列表不存在。Danny Goodman的DHTML: The Definitive Reference 中有最完整的信息。quirksmode网站上PPK的Event Compatibility Table有最好的兼容性评价。YUI的事件功能不对你要附加处理器(监听)的事件做任何的限制,它会尝试地监听你提供的任何事件。所以,确保你针对开发的浏览器能兼容你提供的事件就是你的责任了。
2. 处理器(handler):当事件发生时调用的函数的引用。
3. 元素:如前面所说,这可以是一个或多个选择器语法的引用、Node实例、DOM元素的引用。
4. 执行环境对象:对为事件处理函数提供执行环境的对象的引用——也就是函数中this的指向。如果省略该参数,执行环境对象就是事件目标元素的Node实例。
以下是这些参数的应用实例代码:
Item one
Item two
Item three
Item four
//配置YUI实例
YUI().use("dump", "node-base", function(Y) {
//随便创建一个执行环境对象
var contextObj = {
name: "context"
};
//处理点击事件的函数,输出报告信息
function handleClick(e, arg1, arg2) {
Y.log("Context object:" + Y.dump(this));
Y.log("Event facade object:" + Y.dump(e));
Y.log(arguments);
}
//让单数列表项订阅点击事件
Y.on("click", handleClick, "#list .odd", contextObj, "argumentOne", "argumentTwo");
});
运行这些代码,点击列表的第一个和第三个,Y.log会输出以下信息:
Context object: {name => context}
Event facade object: {
altKey => false,
cancelBubble => false,
ctrlKey => false,
//and remaining event facade properties
...}
最后输出的arguments对象,会包含以下三个项目:
e(event facade)、”argumentOne”(字符串)、“argumentTwo”(字符串)
以前版本的方法:Event.addListener和Event.on
YUI的事件功能包含两个添加事件监听器的两个老的方法:这两个方法包含在Event包中,遵循YUI以前版本YUI的事件功能的语法。如果你使用YUI3.x,应该避免使用这些老的方法,而是使用上面的方法。
移除事件
移除事件监听器有3种方式:
1. 为事件名参数添加事件分类前缀,在移除事件监听器时将这个字符串传给YUI的detach方法。
2. 在事件处理器对象上调用detach方法:on方法的返回值是一个事件处理器对象,该对象有一个detach方法,可以用以移除对应的事件监听器。
3. 调用YUI的detach方法,传入事件名、处理函数、元素三个参数:detach是YUI实例的方法,当你访问不到事件处理器对象时,可以使用该方法。
示例代码:
//配置YUI实例
YUI().use('node-base', function(Y) {
//定义一个事件处理函数
function handleClick(e) {
Y.log(e);
}
// 为foo元素添加事件处理器。事件名中的‘eventcategory|’部分可在移除监听器时用以识别移除哪一个事件的监听器。
var fooHandle = Y.on("eventcatgory|click", handleClick, "#foo");
// 通过事件名的前缀移除事件监听器
Y.detach('eventcategory|click');
// 通过事件处理器对象移除事件监听器
fooHandle.detach();
// 移除‘eventcategory’分类下所有的事件监听器
Y.detach('eventcategory|*');
// 通过detach方法移除
Y.detach("click", handleClick, "#foo");
// 为detach传入事件处理对象也可以移除
Y.detach(fooHandle);
});
模拟事件
模拟出来的事件是浏览器创建的事件。大部分情况下,他们的行为和用户触发的事件是一样的。模拟的事件会冒泡,事件对象有包含关于该事件数据的属性。有时候这些属性是某些浏览器专有的,所以,建议你使用Y.Event的跨浏览器方法来获取这些属性正确的值,比如target、relatedTarget、和charCode。在事件发生的过程中,事件处理器都同步地在事件目标上被调用。我们使用Y.Node实例的simulate方法来模拟事件。
鼠标事件
以下是可以模拟的7个鼠标事件:
click
dbclick
mousedown
mouseup
mouseover
mouseout
mousemove
每一个事件都是在调用simulate方法是被触发,触发时会传入两个参数:触发的事件名和指定事件额外信息的可选的对象。比如,要模拟页面body元素的点击事件,代码如下:
//配置YUI实例
YUI().use('node-event-simulate', function(Y) {
Y.one("body").simulate("click");
});
以上的代码模拟body上的click事件,事件对象上包含所有默认的属性。要给事件指定额外的信息(比如Shift键按下),必须使用第二个参数并为事件属性指定准确的DOM名(在必要时,跨浏览器逻辑会处理跨浏览器的问题)。
YUI().use('node-event-simulate', function(Y) {
Y.one("body").simulate("click", { shiftKey: true });
});
在上面更新后的代码中,一个按下Shift键并点击body元素的事件被模拟了。
根据模拟的事件的不同,需要提供的额外的属性也不同,但仅限于以下列表:
detail – 指定一个按钮被点击的次数(仅限于DOM-compliant浏览器)
screenX/screenY – 相对于屏幕,鼠标事件发生的坐标(仅限于DOM-compliant浏览器)
clientX/clientY – 相对于客户端区域,鼠标事件发生的坐标(仅限于DOM-compliant浏览器)
ctrlKey/altKey/shiftKey/metaKey – Ctrl, Alt, Shift, Meta键的状态,true为按下,false为弹起
button – 触发事件的鼠标按钮,0是左键,1是右键,2是中键
relatedTarget – 鼠标从哪个元素移动到目标元素(在mouseover和mouseout事件中)
使用不同的方法和额外属性的实例:
YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myDiv");
//模拟按下Alt 键并点击
node.simulate("click", { altKey: true});
//模拟按下Ctrl 键双击
node.simulate("dblclick", { ctrlKey: true });
//模拟鼠标经过
node.simulate("mouseover", { relatedTarget: document.body });
//模拟鼠标离开
node.simulate("mouseout", { relatedTarget: document.body });
//模拟在客户端的 (100,100) 点上按下鼠标
node.simulate("mousedown", { clientX: 100, clientY: 100 });
//模拟在客户端的 (100,100) 点上弹起鼠标
node.simulate("mouseup", { clientX: 100, clientY: 100 });
//模拟在客户端的 (200,200) 点上鼠标经过
node.simulate("mousemove", { clientX: 200, clientY: 200 });
});
键盘事件
以下是可以被模拟的键盘事件:
keyup
keydown
keypress
和鼠标一样,键盘事件也是用simulate方法来模拟。对于keyup和keydown事件,必须指定keycode属性。对于keypress事件,必须指定charCode属性。许多情况下,keyCode和charCode会用相同的值来代表相同的按键。(比如,97代表”A”键,也是字母“a”的ASCII码)。
例子:
YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myDiv");
//模拟按下A键
node.simulate("keydown", { keyCode: 97 });
//模拟弹起A键
node.simulate("keyup", { keyCode: 97 });
//模拟用键盘上打出 "a"
node.simulate("keypress", { charCode: 97 });
});
键盘事件还支持ctrlKey, altKey, shiftKey,metaKey事件属性。
注意:因为浏览器实现不同,键盘事件在不同浏览器中的实现方式也是不同的。比如,当在一个输入框中模拟按键按下事件,只有火狐会用按键代表的字符更新输入框。而在其他的浏览器中,事件也注册了,事件处理函数也被调用了,但是输入框中的字符和value属性都没有更新。将来这些浏览器改进对键盘事件模拟的支持后,这些问题会消失的。
UI事件
以下是可以被模拟的UI事件:
blur
change
focus
resize
scroll
select
和其他一样,UI事件也是使用simulate方法模拟的。因为UI事件没有额外的属性,所以模拟UI事件不需要给事件指定额外的信息。以下是一些实例:
YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myInput");
//simulate a change event
node.simulate("change");
//simulate a select event
node.simulate("select");
});
使用available事件和contentready事件(原来的onAvailable方法和onContentReady方法)
available让你可以定义一个函数,一旦DOM中检测到某元素时该函数就执行。这样的目的是为了减少渲染script和HTML时的时间先后问题。该事件不是为了用来给有可能存在于页面中的元素添加事件处理器的,而是为了用来在加载过程中检测元素是否可用。
该事件的用法如下:
YUI().use('node-base', function(Y) {
function TestObj(id) {
Y.on('available', this.handleOnAvailable, id, this);
}
TestObj.prototype.handleOnAvailable = function(me) {
Y.log(this.id + " is available");
}
var obj = new TestObj("myelementid");
});
my element
使用contentready事件的语法和使用available事件的语法一样,这两个事件不同的地方在于contentready会等到目标元素和它的下一个兄弟元素能响应getElementById后才触发该事件。这保证了目标元素的内容(后来通过脚本加载的内容除外)的完全加载。如果contentready检测不到目标元素的下一个兄弟元素,它将触发window.load事件。
使用domready事件(原来的onDOMReady)
domready自定义事件让你定义一个函数,该函数在页面的DOM达到一个可用的状态执行。DOM在结构完整时,才会达到可用的状态;有几个bugs,首先在IE下,如果在DOM达到结构完整时的可用状态之前,尝试通过脚本往DOM中插-入内容的话,会引起浏览器崩毁或者是页面不能成功加载。
DOM在图片加载完成之前,也可达到可用状态。所以,domready事件是window对象的load事件的一个完美替代。
YUI().use('node-base', function(Y) {
function init() {
Y.one("#hidden_element").set("visibility", "");
}
Y.on("domready", init);
// 对于所有的自定义事件,你都可以传入执行环境对象和参数,执行对象和参数最终会被传递给处理函数;
// Y.on("domready", init, contexObject, argumentOne, argumentTwo, argumentN);
});
使用key事件
key事件让你定义一个函数,该函数在某键(或组合键)被按下时执行(不管是不是有功能键【modifier】)。
创建一个按键监听器
下面的代码会为一个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);
// 阻止事件冒泡和阻止事件stopPropagation() and preventDefault()
e.halt();
// 删除订阅,所以该事件只发生一次
handle.detach();
// 事件监听器添加到元素'text1',指定按下事件,指定keycode为13,指定Y为执行环境对象,添加参数
}, '#text1', 'down:13', Y, "arg1", "arg2", "etc");
});
定义按键监听器的细节
在前面的例子中,’down:13′定义了按键事件监听的细节。这指定了只有返回键触发了按键按下事件,监听器才执行。指定的字符串包含三部分:
1:事件名后跟着一个冒号(‘up:’, ‘down:, or ‘press:’)
2:0个或多个被监听的keyCode,由逗号分隔。如果定义多于一个keyCode,则任意个一keyCode被监测倒都会使监听器开始执行。
3:0个或多个被监听的keyCode,由加号分隔。如果定义了功能键(modifier key),所有的键都被监测到才会使监听器开始执行。
所以,’press:65,66+shift+ctrl’这样的定义,在一个按键按下,shift和control键被按下,并且检测到keyCOde65或66时,才会触发监听器开始执行。
使用delegate方法
事件代理是指在容器元素上添加事件处理器,用以监听后代元素的交互动作。因为子元素的事件会冒泡传播到容器元素,所以这是减少事件处理器数量的稳定有效的策略(关于事件监听的更多内容请看这篇YUI博客上的文章)。
YUI的事件功能提供了一个delegate方法,可以使用CSS选择器语法来定义代理容器元素中首监听器监听的元素,这使事件代理的应用更简单了。
创建一个代理的事件监听器
考虑以下HTML代码:
Item Type One
Item Type Two
Item Type Three
使用Y.delegate方法,将要代理的事件名作为第一个参数,跟着就是监听函数,作为代理父元素的节点(监听器添加到的元素节点),最后是子元素节点,只有事件的目标元素与这些子节点匹配的时候,监听函数才会被调用。
下面的例子是绑定一个点击事件监听器到容器节点(
),但只有事件目标是
的时候,监听函数才会别调用。
YUI().use("event-delegate", function(Y) {
Y.delegate("click", function(e) {
// 默认情况下,this对象指向匹配的li元素
Y.log("Default scope: " + this.get("id"));
// 如果this对象在订阅的过程中被覆盖了,该li元素也可以通过event的currentTarget属性访问到
Y.log("Clicked list item: " + e.currentTarget.get("id"));
// 真正的点击目标,可能是匹配的li或者它的子元素
Y.log("Event target: " + e.target);
// 代理父元素被添加到event facade对象中。
Y.log("Delegation container: " + e.container.get("id"));
}, "#container", "li");
});
Nodes上也有delegate方法,所以以下是另一种语法:
YUI().use("node", function(Y) {
var container = Y.one("#container");
container.delegate("click", function (e) {
// Same as above
}, "li");
});
使用focus事件和blur事件
DOM的focus事件和blur事件不会冒泡。使用YUI的事件功能的fucus和blur事件可以为能获取焦点的元素分别添加focus和blur事件处理器。focus和blur事件监听DOM事件的捕获阶段(IE的focusin和focusout),使我们可以给一个元素添加一个事件监听器,就可以监听该元素中能获取焦点的后代元素触发的focus和blur事件。减少事件处理器被证明是提升页面性能的好策略,所以,使用focus和blur事件能帮助监听这俩事件的页面或应用提升性能。
创建一个focus监听器
考虑以下HTML代码:
要监听工具条中每一个按钮,传统的做法是为每一个按钮添加事件监听器。然而,使用YUI的事件功能让我们只需要为容器元素(在这里是)添加一个监听器,任何一个按钮获取焦点时我们都会被告知。
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);
// 将监听器添加给id为"toolbar"的元素,指定Y为执行环境对象,添加参数
}, "#toolbar", Y, "arg1", "arg2", "etc");
});
使用mouseenter事件和mouseleave事件
从IE的mouseenter和mouseleave事件得到启发, YUI的事件功能提供在所有A级浏览器中监听mouseenter和mouseleave的功能。对于响应mouseover和nouseout事件的DOM操作来说,使用mouseenter和mouseleave事件可以提升性能,因为这两个事件不会冒泡,对DOM的改变的频率会小很多。
创建一个mouseenter事件监听器
考虑以下HTML代码:
Item Type One
Item Type Two
Item Type Three
下面的脚本会将mouseenter事件监听器和mouseleave事件监听器添加给容器元素(
)。当鼠标第一进入容器元素时,mouseenter事件监听函数会被调用。当鼠标第一次离开容器元素时,mouseleave事件监听函数会被调用。
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");
});
使用touch事件
YUI的DOM事件还扩展到touch事件。要在你的应用中使用touch事件,你需要把event-touch模块包含进你的代码语句中。
YUI支持以下常用的低级touch事件,这些事件存在于大部分的有触摸功能的操作系统中:
touchstart
touchmove
touchend
touchcancel
另外,YUI也支持苹果iOS中特有的guesturestart、guesturechange、guestureend事件。目前,在其他的操作系统中,YUI还没有提供对这些事件的支持,因为它们还缺少对DOM层级多点触摸的支持。一旦这些操作系统公开在低级touch事件中的多点触摸相关信息后,我们就可以加入对多点触摸手势(multi-touch gestures)的跨平台支持。
你可以像为其他DOM事件添加监听器一样为touch事件添加监听器。在前面的DOM Events部分有说明。
touch事件的 event facade
传递给touch事件的event facade有触摸事件特有的属性:
touches
changedTouches
touchTargets
这些事件对象和传递给其他DOM监听函数的事件对象一样都是规范的。
和本地的事件对象一样,iOS中的手势事件(guesture event)的事件对象有scale和rotation属性。
跨设备的手势支持
event-guesture模块提供一组自定义事件(synthetic events),用以描述用户常用的交互手势。这组自定义事件在触摸输入设备和鼠标输入设备中都能正常工作。
编写支持触摸和鼠标输入设备的代码的开发人员就可以使用这些手势事件。拖放组件(DD)就是一个很好的例子。我们基于手势层(特别是下面说到的手势移动事件)创建拖放组件,所以在触摸设备和鼠标设备上都支持拖放组件的所有功能。
拖放组件针对手势事件编程,不管start、move、end事件是由触摸设备还是鼠标触发的。所以,同样的拖放代码在触摸设备或鼠标上都正常工作。
event-guestures模块目前有两个子模块,event-flick模块和event-move模块。将来还会添加更多跨设备的手势模块。
flick 手势事件
当用户在目标元素上发起一个flick手势flick(用手指轻弹)手势事件时,flick guesture事件被触发。
要使用flick事件,你需要使用event-flick模块,然后你就可以像监听其他DOM事件一样监听flick事件:
myNode.on("flick", function(e) {
// event facade对象上的filck事件信息
var flick = e.flick,
velocity = flick.velocity,
distance = flick.distance,
axis = flick.axis,
startX = flick.start.pageX,
startY = flick.start.pageY,
// The event object itself is the event object for
// 结束flick手势的事件(mouseup 或 touchend)
endX = e.pageX,
endY = e.pageY,
endTarget = e.target;
});
在订阅flick事件时,你可以通过第三个参数传入额外的配置信息,这些配置信息控制订阅者(监听器)得到通知的时间和方式。
// 自定义配置、没有执行环境对象、没有额外参数
myNode.on("flick", flickHandler, {
// flick超过20px而且速度大于0.8 px/ms时再发出通知
minDistance:20,
minVelocity:0.8,
// 阻止鼠标事件和触摸事件潜在的行为
preventDefault:true
});
// 自定定义配置、使用bind方法指定执行环境、额外的参数
myNode.on("flick", Y.bind(o.flickHandler, o, arg1), {
minDistance:20,
minVelocity:0.8,
preventDefault:true
});
// 使用老方法绑定执行环境和额外参数
// Flick总是预期第三个参数是flick的配置,所以不使用flick配置时,需要传入null代替。使用bind方法更清晰可靠。
myNode.on("flick", o.flickHandler, null, o, arg1);
配置参数在flick event的API文档中有详细说明。
flick事件目前不能使用事件代理。
move手势事件
move是一个更高级别的手势,该手势封装交互的开始和结束。
guesturestart、gusturemove、guestureend事件都是低级别的手势事件,用来识别鼠标或手指的”move”手势的开始、进行、结束。它们可以用作识别更高级手势的基本材料,比如”swipe”手势,详见Swipe Gesture实例。
要使用move手势事件,你需要在YUI实例中载入event-move模块,该模块支持上述的三个事件:
// 当用户把手指放在元素上或者在元素上按下鼠标时通知我
myNode.on("gesturemovestart", function(e) {...}, {
// 等待1000毫秒或者等待手指/鼠标移动超过3px,然后触发事件。
minDistance: 3,
minTime:1000,
// 如果目标元素不是select元素,阻止默认行为。
preventDefault:function(e) {
return (e.target.get("tagName").toLowerCase() !== "select");
}
});
// 当用户移动手指或者鼠标时通知我(前提是目标元素之前接收到gesturemovestart事件)。
myNode.on("gesturemove", function(e) {...});
// 当用户移动手指或者鼠标时通知我,即使document元素没有接收过gesturemovestart事件(standAlone:true)。
Y.one("document").on("gesturemove", function(e) {...}, {
standAlone:true
});
// 当用户抬起手指或者松开鼠标按钮(前提是目标元素之前接收到过gesturemovestart 事件)。
myNode.on("gesturemoveend", function(e) {...});
和flick事件一样,必须为监听器配置on方法的第三个参数。完整的配置参数说明在三个gesture move events的API文档中。
和flick事件不同,move事件可以使用事件代理(如果使用事件代理的话,配置对象从第四个参数传入,第三个参数是代理元素的过滤器)。
相关的事件
值得注意的是,三种move手势事件之间的关系和flick的不同(它们是对同一个手势的start、progress、end状态的通知)。
当编写一个需要用到move手势事件的应用时,在’start’的监听器接收到通知之前,你不希望’move’监听器收到通知;在’start’或者’move’监听器接收到通知之前,你不希望’end’监听器收到通知。这是绑定在同一个元素上的move事件的默认行为——在同一个元素上,start监听器接收到通知后,’move’和’end’监听器才会接收到通知。
然而,如上面的例子,如果你希望不管是否发生过guesturestart事件,guesturemove和guestureend时间的监听器都能得到通知(比如你动态地添加/删除后面的监听器),就可以使用standAlone:true配置。在内部,DOM监听器会监视默认绑定到document元素的mousemove/touchmove 和 mouseup/touchend。节点元素提供共享的执行环境对象来关联这三个事件。
创建虚构的DOM事件(Synthetic DOM Events)
YUI事件系统可以为元素节点定义DOM原生事件以外的新的事件。和自定义事件(custom events)类似,这些事件允许你为标识那些有意思的瞬间;和自定义事件不一样的是,虚构事件是与页面相关的,而不是与你创建的任何应用类相关。本质上,这些事件是扩展了那些能在元素节点上订阅的DOM事件。
创建虚构事件的API接口是Y.Event.define(name,config);
YUI().use("event-synthetic", function(Y) {
// 创建名为 "clickoutside"的DOM事件
Y.Event.define("clickoutside", {
// 当有人订阅这个事件时,这个函数执行
on: function (node, subscription, notifier) {
function outside(clickTarget) {
return clickTarget !== node && !clickTarget.ancestor(
function (parent) {
return parent === node;
});
}
// 安排通知器当满足某些条件时,广播clickoutside事件。典型的做法就是订阅原生DOM事件加上一些逻辑判断。
var handle = Y.one('doc').on('click', function (e) {
// 只有用户点击订阅的Node元素或者它的子元素外边时,监听函数才执行。
if (outside(e.target)) {
// 将发起该事件的原始DOM事件传递给订阅者(监听函数)
notifier.fire(e);
}
});
// 在订阅对象上储存状态数据
subscription.clickHandle = handle;
},
// 当订阅取消时,该函数被执行
detach: function (node, subscription, notifier) {
// 清除on阶段添加的DOM订阅
subscription.clickHandle.detach();
},
// 还可以定义
delegate: function (node, subscription, notifier, filter) {
...
},
// 和delegate对应的
detachDelegate: function (node, subscription, notifier, filter) {
...
}
});
// Subscribe to the synthetic event as you would any
// native DOM event
Y.on("clickoutside", function (e) {
this.addClass('hidden');
}, "#menu");
其他高级配置选项详见API文档。
使用自定义事件
YUI自定义事件系统让你可以定义和使用DOM事件以外的事件——专门针对你的应用、有利于你的应用的事件。自定义事件被设计成像DOM事件一样工作。他们可以冒泡、传递event facade、抑制传播和默认行为等。这部分叙述了YUI自定义事件的几个常见应用,还有一些实例代码。
使用Y.on的一些简单自定义事件
你可以在你代码的任何地方调用YUI实例的fire方法,发布某个有意思的瞬间的事件。
//配置YUI实例:
YUI().use('event-custom', function(Y) {
Y.fire('customapp:started', 1, 2, 3);
});
这段代码会发送通知给订阅过那个事件的函数(监听函数):
Y.on('customapp:started', function(arg1, arg2, arg3) {
Y.log('Custom App Started, now I can do a a few things);
// 参数1,2,3是由fire()提供的
});
在一个Event target的基础上定义自定义事件
创建自定义事件的常用方法就是用EventTarget augment一个对象,让这个对象成为自定义事件的宿主,成为从其他宿主冒泡过来的自定义事件的目标:
YUI().use('event-custom', function(Y) {
// 定义一个构造函数
function Publisher() {
// 创建一个自定义事件。除非你需要覆盖某事件的默认配置,否则没有必要明确地发布一个事件
this.publish("publisher:testEvent", {
// 该事件的配置项目
});
}
// 用EventTarget augment Publisher
Y.augment(Publisher, Y.EventTarget, null, null, {
//这个参数被提供给EventTarget的构造函数,让你设置在这个event target上发布的每个事件
});
// 如果你接受默认的配置,augment EventTarget如下
// Y.augment(Publisher, Y.EventTarget);
});
publish构造函数创建一个新的自定义事件。它接收一个必须参数和一个可选参数。
type – 事件名。该字符串会被传给这个是假你的监听函数,这样监听函数才知道发生可什么。
options – 你想为该自定义事件定义的配置选项。自定义时间类的大部分属性都可以在这时设定。
订阅(监听)自定义事件
要订阅一个自定义事件,需要使用on方法。下面的代码将会订阅publisher:testEvent事件:
var publisher = new Publisher();
publisher.on("publisher:testEvent", function(e) {
//事件处理函数代码
});
触发事件
要触发一个自定义事件,只需调用fire访法:
publisher.fire("publisher:testEvent");
在YUI实例之间广播事件/共享信息
当发布一个事件时,你可以指定一个广播配置——这样任意代码都知道这个事件。你可以把事件通知限制在YUI实例中,也可以全局地发布通知,让页面上的任意代码都能使用这个事件。这是在不同的YUI沙盒之间共享数据的主要办法。
var publisher = new Y.EventTarget();
publisher.name = 'broadcast publisher';
publisher.publish('instance_notification:foo', {
broadcast: 1, // 在实例级别发布事件通知
emitFacade: true // 发出facade,让我们可以访问事件目标
});
// 这个事件是可用的,可以用YUI实例订阅它
Y.on('instance_notification:foo', function(e) {
Y.log(e.target.name); // broadcast publisher
});
var publisher2 = new Y.EventTarget();
publisher2.name = 'global publisher';
publisher.publish('global_notification:foo', {
broadcast: 2, // 全局地发布事件通知
emitFacade: true // 发出facade,让我们可以访问事件目标
});
// 外部的代码,这个新的沙盒可以访问到全局的事件
YUI().use('event-custom', function(Y2) {
// 在一个特殊的event target引用上监听这个全局的事件,这个特殊的event target在YUI实例上,名为Global。
Y2.Global.on('global_notification:foo', function() {
Y.log(e.target.name); // global publisher
});
Y2.on('instance_notification:foo', function(e) {
// 将收不到通知
});
};