作者:
Truly
日期: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);
而在另外一个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');
}
通常我们经常要处理window.onload事件,例如使用下面代码来指定onload事件
window.onload
=
aFunction
而当我们这样的方式声明的时候,很可能会覆盖已经定义过的window.onload事件,或者我们这里还有很多事件要在onload执行,那么如何应对这种情况呢?Observer模式恰好可以用来处理这种情况。首先我们需要为页面定义了一个监听器,检测页面中需要处理的事件,然后定义一个全局的监听器数组。这样需要处理的事件都可以注册到这个监听器数组中,然后统一进行调用。
如上面代码中的,我们将需要处理的事件名通过下面代码
PageLoadListener.push(domLoaded);
注册到Listener数组中,这样的注册过程可能遍布到不同的js文件或脚本块中,最后使用监听器集中对数组中的元素进行调用,这样以来就很好的解决了window.onload事件冲突的问题。
Obsever进阶应用
下面我们演示一个更加复杂的Obsever模式应用,来自著名的Prototype框架,请先查看代码:
Hello.htm
<
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')));}
);
上面代码演示了一个当选择下拉框的时候,调整文本框的值。我们演示了onchange和onload事件的监听,同样的也可以应用到任何DOM节点的各个事件上。但是,你可能说你可以在<select>标签中直接添加onchange事件就可以了,为什么要这么做?
Well,首先这样可以更好的分离代码和视图,就像我上篇文章中讨论的MVC模式,我们应该尽可能的分离代码和视图。尤其是当你构建一个大型的应用程序的时候,例如飞鸽这样的网站,越是可以从中受益。
同时通过这种方式,可以设计出一个完整的客户端事件流程。关于JavaScript事件模型的讨论,将是我们后面文章的讨论内容。
注:文中代码部分取自著名的Prototype框架,不过根据行文需要,我做了适当改动 :)