晚上接下去写event这一模块。。。这一篇可能要详细的讲讲YUI中的观察者模式
二、event模块
首先看下YUI封装的Event事件绑定机制:
最核心的一个函数便是addListener,这个函数除了第一个需要传入元素外其他形式参数的和jQ的bind一模一样,这里还是要注意第一个元素传入可以是id值数组
使用方法也比较的明了,注意YUI这里定义有一个简写为on的方法指向这个方法
var oElement = document.getElementById("flowg");
function callback(e) { alert("hello"); }
YAHOO.util.Event.addListener(oElement, "click", callback);
如上面说的这边的el也是可以传入一个数组引用的,下面的代码便是对于数组每个元素进行on方法迭代:
if ( this._isValidCollection(el)) {//判断是否为数组容器
var ok = true;
for (var i=0,len=el.length; i<len; ++i) {//迭代
ok = this.on(el[i], sType, fn, obj, override) && ok;
}
return ok;
}
这个绑定事件方法可以在DOM文档未加载完前调用,因为这个方法会自动判断是否加载完全,若否则会等待加载好了在执行,相应代码如下:
else if (YAHOO.lang.isString(el)) {
var oEl = this.getEl(el);//这边的getEl就是ById方法的封装,可能是想保持模块间的松耦合,这边没有调用DOM模块的get方法
if (oEl) {//直接查找到的时候
el = oEl;
} else {
// 待载入完成之后再查找一次
this.onAvailable(el, function() {
YAHOO.util.Event.on(el, sType, fn, obj, override);
});
return true;
}
}
接下去是绑定事件了,绑定事件的源码就不贴了,大致看了下也是用到了缓存数组存贮监听事件。
类似于jQ的unbind YUI也有,叫removeListener,把上面的callback解除绑定:
YAHOO.util.Event.removeListener("flowg", "click", callback);
最后一个参数不设置的话,就会吧该元素的所有click监听器全部解绑。
再看一个比较牛叉的方法 purgeElement(),把该元素的指定的所有监听函数全部移除。
// 移除改元素绑定的所有监听器函数
YAHOO.util.Event.purgeElement(ele);
// 移除该元素所有的子元素的绑定的监听器(这个务必慎用啊)
YAHOO.util.Event.purgeElement(ele, true);
// 只移除该元素上click事件的监听函数
YAHOO.util.Event.purgeElement(ele, false, "click");
接下去又是jQ中万能的$的ready方法YUI版本要出场了。
这里有三个有点相似的方法:
onAvailable:当元素可用时触发,这个方法是有轮询机制的,默认状况下是10m内按照4000一次的频率轮询搜索元素;
onAvailable ( id , fn , obj , overrideContext , checkContent )这边最后的checkContent是查看子元素是否就绪,如果将它设为true则就是onContentReady这个方法了:
onContentReady: function(p_id, p_fn, p_obj, p_override) {
this.onAvailable(p_id, p_fn, p_obj, p_override, true);
},
而接下来的onDOMReady便是jQ的万能$的真正YUI版了,在DOM全部载入之后执行,系统会轮询检测DOM是否加载,当加载完毕后会使一个boolean类型Flag令为true,通知onDOMReady订阅者加载函数。这里同以前的一样也要注意,这边最后一个参数是是否用第二个参数作用域代替win作用域,常用与自建包中return的静态方法中:如下(get.js)
YAHOO.util.Event.onDOMReady(
YAHOO.example.SiteExplorer.init, //YAHOO.example.SiteExplorer中return公共静态方法
YAHOO.example.SiteExplorer, //设置作用域
true); //设置为true则是用上面的对象作为上下文
下面是另外一个重点:CustomEvent这个自定义的事件。其中用到了观察者的设计模式
这边直接把官方的实例文档拿过来做一下注释:(感觉这个实例看懂了就基本这个类能够用了)
(function() {
//首先new出一个自定义事件对象,取名为onSizeChange
var onSizeChange = new YAHOO.util.CustomEvent("onSizeChange");
var container = YAHOO.util.Dom.get("container");
var resizer = YAHOO.util.Dom.get("resizer");
function fnClick(e){
//取得相关值
var containerX = YAHOO.util.Dom.getX("container");
var containerY = YAHOO.util.Dom.getY("container");
var clickX = YAHOO.util.Event.getPageX(e);
var clickY = YAHOO.util.Event.getPageY(e);
//更改该元素css
var containerPaddingX = parseInt(YAHOO.util.Dom.getStyle("container","padding-left"), 10);
var containerPaddingY = parseInt(YAHOO.util.Dom.getStyle("container","padding-top"), 10);
var newWidth = clickX - containerX - containerPaddingX;
var newHeight = clickY - containerY - containerPaddingY;
if ((newWidth > 0)||(newHeight > 12)) {
if (newWidth < 0) {newWidth = 1;}
if (newHeight < 12) {newHeight = 12;}
YAHOO.util.Dom.get("resizer").innerHTML = "New size: " + newWidth + "x" + newHeight;
YAHOO.util.Dom.setStyle("resizer", "width", newWidth + "px");
YAHOO.util.Dom.setStyle("resizer", "height", newHeight + "px");
//这边便是重点,发布一个消息,对于onSizeChange中注册的订阅者逐一的进行通知
onSizeChange.fire({width: newWidth, height: newHeight});
};
}
//注册监听器
YAHOO.util.Event.addListener("container", 'click', fnClick);
//这边有两个征订者函数
fnSubscriberWidth = function(type, args) {
var elWidth = YAHOO.util.Dom.get("subscriberWidth");
var newWidth = args[0].width;
YAHOO.util.Dom.setStyle(elWidth, "width", (newWidth + "px"));
elWidth.innerHTML = ("My new width: " + newWidth + "px");
}
fnSubscriberHeight = function(type, args) {
var elHeight = YAHOO.util.Dom.get("subscriberHeight");
var newHeight = args[0].height;
YAHOO.util.Dom.setStyle(elHeight, "height", (newHeight + "px"));
elHeight.innerHTML = ("My new height: " + newHeight + "px");
}
//指定函数订阅这个事件
onSizeChange.subscribe(fnSubscriberWidth);
onSizeChange.subscribe(fnSubscriberHeight);
})();
了解观察者模式对于这种结构的程序理解起来并不难。下面来看一下源码:
首先是订阅消息:
subscribe: function(fn, obj, override) {
/*此处省略一万行*/
this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );//往订阅者队列中插入一个订阅者的实体
},
接下来是分发消息:
fire: function() {
var len=this.subscribers.length;//取得队列长度
var args=[], ret=true, i, rebuild=false;
for (i=0; i<arguments.length; ++i) {//暂存消息信息
args.push(arguments[i]);
}
//开始迭代分发消息给订阅者
for (i=0; i<len; ++i) {
var s = this.subscribers[i];
if (!s) {
rebuild=true;
} else {
if (!this.silent) {
}
var scope = s.getScope(this.scope);//取得域
if (this.signature == YAHOO.util.CustomEvent.FLAT) {
var param = null;
if (args.length > 0) {
param = args[0];
}
try {
ret = s.fn.call(scope, param, s.obj);
} catch(e) {
this.lastError = e;
}
} else {
try {
ret = s.fn.call(scope, this.type, args, s.obj);//这边便是调用订阅者的函数,函数传入形参可以看都是type,arges,obj三个参数
} catch(ex) {
this.lastError = ex;
}
}
if (false === ret) {
if (!this.silent) {
}
//break;
return false;
}
}
}
return true;
},
大概的模式就是这样,然后还有个与之对应的方法,就是取消订阅者unsubscribe这个方法,方法内执行的是将订阅者队列中的对应函数对象移除。
小结下:这篇写的也不是很详细,对有些代码的实现感到还是疑惑,不过YUI中的自定义事件相当经典,可以完全制造出基于逻辑的事件监听,更加符合面向对象的思想,也难怪后来的Ext基本对于此事件处理完全继承下来了~~
有时间下一篇写一下ajax模块~~