作者:Truly
日期:2007.7.31
上次我们讨论了Web开发中最重要的设计模式MVC,今天我们要讨论的是Observer模式,与MVC这样的大型设计模式相比,Observer模式则要轻量很多。废话不多说了,进入主题
Obsever简单应用
请先看一段代码:
而在另外一个js中我们定义:
通常我们经常要处理window.onload事件,例如使用下面代码来指定onload事件
而当我们这样的方式声明的时候,很可能会覆盖已经定义过的window.onload事件,或者我们这里还有很多事件要在onload执行,那么如何应对这种情况呢?Observer模式恰好可以用来处理这种情况。首先我们需要为页面定义了一个监听器,检测页面中需要处理的事件,然后定义一个全局的监听器数组。这样需要处理的事件都可以注册到这个监听器数组中,然后统一进行调用。
如上面代码中的,我们将需要处理的事件名通过下面代码
Obsever.js
function $(id){return document.getElementById(id);}
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}
var Browser={
isWebKit : navigator.userAgent.indexOf('AppleWebKit/') > -1
}
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
if (!window.Event) {
var Event = new Object();
}
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
Object.extend(Event,
{
observe: function(element, name, observer, useCapture) {
if(typeof element != 'object')
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(isWebKit || element.attachEvent))
name = 'keydown';
Event._observeAndCache(element, name, observer, useCapture);
},
stopObserving : function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(Browser.WebKit || element.attachEvent))
name = 'keydown';
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
try {
element.detachEvent('on' + name, observer);
} catch (e) {}
}
},
observers: false,
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element, name, observer, useCapture]);
element.addEventListener(name, observer, useCapture);
} else if (element.attachEvent) {
this.observers.push([element, name, observer, useCapture]);
element.attachEvent('on' + name, observer);
}
}
}
)
Controller.js
日期:2007.7.31
上次我们讨论了Web开发中最重要的设计模式MVC,今天我们要讨论的是Observer模式,与MVC这样的大型设计模式相比,Observer模式则要轻量很多。废话不多说了,进入主题
Obsever简单应用
请先看一段代码:
// the process array calling after page loaded for page listener.
var PageLoadListener = new Array();
// page listener
function onDocumentLoaded()
{
for (var a in PageLoadListener)
{
if(typeof PageLoadListener[a] == 'function')
PageLoadListener[a]();
}
}
// Add a listener to current page to run all function on the page.
if (document.addEventListener)
document.addEventListener('DOMContentLoaded', onDocumentLoaded, false);
else
window.attachEvent('onload', onDocumentLoaded);
var PageLoadListener = new Array();
// page listener
function onDocumentLoaded()
{
for (var a in PageLoadListener)
{
if(typeof PageLoadListener[a] == 'function')
PageLoadListener[a]();
}
}
// Add a listener to current page to run all function on the page.
if (document.addEventListener)
document.addEventListener('DOMContentLoaded', onDocumentLoaded, false);
else
window.attachEvent('onload', onDocumentLoaded);
而在另外一个js中我们定义:
PageLoadListener.push(domLoaded); // push the domLoaded function into the listener array.
// a method need to call after page is loaded
function domLoaded()
{
alert('document loaded');
}
// a method need to call after page is loaded
function domLoaded()
{
alert('document loaded');
}
通常我们经常要处理window.onload事件,例如使用下面代码来指定onload事件
window.onload=aFunction
而当我们这样的方式声明的时候,很可能会覆盖已经定义过的window.onload事件,或者我们这里还有很多事件要在onload执行,那么如何应对这种情况呢?Observer模式恰好可以用来处理这种情况。首先我们需要为页面定义了一个监听器,检测页面中需要处理的事件,然后定义一个全局的监听器数组。这样需要处理的事件都可以注册到这个监听器数组中,然后统一进行调用。
如上面代码中的,我们将需要处理的事件名通过下面代码
PageLoadListener.push(domLoaded);
注册到Listener数组中,这样的注册过程可能遍布到不同的js文件或脚本块中,最后使用监听器集中对数组中的元素进行调用,这样以来就很好的解决了window.onload事件冲突的问题。
Obsever进阶应用
下面我们演示一个更加复杂的Obsever模式应用,来自著名的Prototype框架,请先查看代码:
<html>
<head>
<title>Obsever Demo</title>
<script language="javascript" type="text/javascript" src="Obsever.js"></script>
<script language="javascript" type="text/javascript" src="Controller.js"></script>
</head>
<body>
<input id='textbox1' name='textbox1'/>
<select id='selElement1' >
<option >choose</option>
<option value='1' >1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
</body>
</html>
<head>
<title>Obsever Demo</title>
<script language="javascript" type="text/javascript" src="Obsever.js"></script>
<script language="javascript" type="text/javascript" src="Controller.js"></script>
</head>
<body>
<input id='textbox1' name='textbox1'/>
<select id='selElement1' >
<option >choose</option>
<option value='1' >1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
</body>
</html>
Obsever.js
function $(id){return document.getElementById(id);}
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}
var Browser={
isWebKit : navigator.userAgent.indexOf('AppleWebKit/') > -1
}
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
if (!window.Event) {
var Event = new Object();
}
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
Object.extend(Event,
{
observe: function(element, name, observer, useCapture) {
if(typeof element != 'object')
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(isWebKit || element.attachEvent))
name = 'keydown';
Event._observeAndCache(element, name, observer, useCapture);
},
stopObserving : function(element, name, observer, useCapture) {
element = $(element);
useCapture = useCapture || false;
if (name == 'keypress' &&
(Browser.WebKit || element.attachEvent))
name = 'keydown';
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
try {
element.detachEvent('on' + name, observer);
} catch (e) {}
}
},
observers: false,
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element, name, observer, useCapture]);
element.addEventListener(name, observer, useCapture);
} else if (element.attachEvent) {
this.observers.push([element, name, observer, useCapture]);
element.attachEvent('on' + name, observer);
}
}
}
)
Controller.js
function changeHandler()
{
$('textbox1').value=this.value;
}
Event.observe(window, 'load',
function(){Event.observe('selElement1','change',changeHandler.bind($('selElement1')));}
);
{
$('textbox1').value=this.value;
}
Event.observe(window, 'load',
function(){Event.observe('selElement1','change',changeHandler.bind($('selElement1')));}
);
上面代码演示了一个当选择下拉框的时候,调整文本框的值。我们演示了onchange和onload事件的监听,同样的也可以应用到任何DOM节点的各个事件上。但是,你可能说你可以在<select>标签中直接添加onchange事件就可以了,为什么要这么做?
Well,首先这样可以更好的分离代码和视图,就像我上篇文章中讨论的MVC模式,我们应该尽可能的分离代码和视图。尤其是当你构建一个大型的应用程序的时候,例如飞鸽这样的网站,越是可以从中受益。
同时通过这种方式,可以设计出一个完整的客户端事件流程。关于JavaScript事件模型的讨论,将是我们后面文章的讨论内容。
注:文中代码部分取自著名的Prototype框架,不过根据行文需要,我做了适当改动 :)