jquery下組織javascript代碼(js函數(shù)化)
更新時(shí)間:2010年08月25日 19:10:46 作者:
這里整理的是jquery下js的一些代碼組織方法,大家可以借鑒下整理出基于jquery的自己喜歡的模式。
從神奇的"$"函數(shù)開(kāi)始
"$"函數(shù)將在文檔加載完成之后為一個(gè)指定的button 綁定事件,這些代碼在單個(gè)網(wǎng)頁(yè)中工作正常。但是如果我們還有其它的網(wǎng)頁(yè),我們將不得不重復(fù)這個(gè)過(guò)程。
<a href="javascript:;" id="sayHello">Say Hello</a>
<script type="text/javascript">
//when dom ready, do something.
//bind click event to a button.
$(function(){
$('#sayHello').click(function(){
alert('Hello world!');
});
});
</script>
如果我們需要另一個(gè)行為的button怎么辦?比如象這樣:
<a href="javascript:;" id="sayUnlike">Unlike it</a>
<script type="text/javascript">
//when dom ready, do something.
//bind click event to a button.
$(function(){
$('#sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
接下來(lái),更多的問(wèn)題出現(xiàn)了,我們需要很多這樣的button, 這好象也不難。
<a href="javascript:;" class="sayUnlike">Unlike it</a>
<script type="text/javascript">
//Change to a class selector to match all the button elements.
$(function(){
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
一個(gè)頁(yè)面里面同種出現(xiàn)了兩種button ......
<a href="javascript:;" class='sayHello'>Say Hello</a>
<a href="javascript:;" class="sayUnlike">Unlike it</a>
<script type="text/javascript">
$(function(){
$('.sayHello').click(function(){
alert('Hello world!');
});
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
但是呢,不是所有的頁(yè)面都會(huì)用到這兩種的button,為了不在頁(yè)面上使用額外的選擇器,我們要作一些必要的調(diào)整,因?yàn)榛赾lass的選擇器的性能相對(duì)于id選擇器開(kāi)銷很大,需要遍歷所有dom元素,并使用正則表達(dá)式匹配class屬性來(lái)選定滿足條件的元素。
<? if($page == 'A'){?>
<script type="text/javascript">
$(function(){
$('.sayHello').click(function(){
alert('Hello world!');
});
});
</script>
<? } ?>
<? if($page == 'B'){?>
<script type="text/javascript">
$(function(){
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
<? } ?>
我們的項(xiàng)目功能越來(lái)越復(fù)雜,經(jīng)過(guò)一段時(shí)間以后,變成了這個(gè)樣子, quick but dirty......
<? if($page == 'A' or $page == "C" and $page is not "D"){ ?>
<script type="text/javascript">
......
</script>
<? } ?>
<? if($page == "B" or $page == "E" and $page is not "X"){ ?>
<script type="text/javascript">
.....
</script>
<? } ?>
<? if($page == "B" or $page == "E" or $page == "C"){ ?>
<script type="text/javascript">
.....
</script>
<? } ?>
這真是太糟糕了,我們需要在一個(gè)頁(yè)面上加載許多個(gè)代碼片斷才能綁定所有的事件,如果我們?cè)賹⒉煌拇a分裝入多個(gè)js文件中這將增加多個(gè)頁(yè)面資源的http請(qǐng)求,不論是管理還是用戶體驗(yàn)都將面臨挑戰(zhàn),我們需要找到一個(gè)更佳的解決方案。
既然 class selector 的開(kāi)銷這么大,我們能不能在一次掃描中綁定所有的事件?我們可以嘗試一下:
<script type="text/javascript">
//Register global name space.
var Yottaa = Yottaa || {};
Yottaa.EventMonitor = function(){
this.listeners = {};
}
//Bind all event.
Yottaa.EventMonitor.prototype.subscribe=function(msg, callback){
var lst = this.listeners[msg];
if (lst) {
lst.push(callback);
} else {
this.listeners[msg] = [callback];
}
}
// Create the event monitor instance.
var event_monitor = new Yottaa.EventMonitor();
function load_event_monitor(root){
var re = /a_(\w+)/; //using a regular expression to filter all event object.
var fns = {};
$(".j", root).each(function(i) {
var m = re.exec(this.className);
if (m) {
var f = fns[m[1]];
if (!f) { //如果事件處理函數(shù)不存在則創(chuàng)建函數(shù)對(duì)象.
f = eval("Yottaa.init_"+m[1]);
fns[m[1]] = f;//調(diào)用綁定函數(shù).
}
f && f(this);
}
});
}
$(function(){
// when dom ready, bind all event.
load_event_monitor(document);
});
//Here is 2 sample components.
Yottaa.init_sayhello = function(obj){
$(obj).click(function(){
alert('Hello world!');
});
}
Yottaa.init_unlike = function(obj){
$(obj).click(function(){
alert('I unlike it.');
});
}
</script>
我們的DOM元素這樣寫(xiě):
<a href="javascript:;" class="j a_sayhello">Say Hello</a>
<a href="javascript:;" class="j a_unlike">Say Unlike</a>
這樣看起似乎好多了,我們只需要在頁(yè)面加載的時(shí)候執(zhí)行一次class selector(在上面的代碼中就是所有'.j'的元素)就可以找到所有需要綁定事件的元素,具體綁定哪一個(gè)組件由 class 名稱里面的 a_xxx 來(lái)決定,對(duì)應(yīng)著 Yottaa.init_xxx,并將當(dāng)前元素的引用作為參數(shù)傳入事件邏輯中。
在這個(gè)處理模式下,我們不需要再次手動(dòng)編寫(xiě)事件處理的邏輯并將它放到 $(function(){ .... }); 這樣的初始化函數(shù)中,所有我們要做的事情僅僅是給組件的“容器”加上兩個(gè) class: "j a_XXX"程序即可幫我完成事件綁定工作,是不是很 cool ?象常用的展開(kāi)/折疊效果,全選/反選效果, tab切換以致于一些其它的簡(jiǎn)單功能都可以使用這種方式。難道這就是傳說(shuō)中的銀彈?不,事情沒(méi)那么簡(jiǎn)單,我們應(yīng)該看到這種處理方式一些弱點(diǎn):
不能給組件傳遞初始化參數(shù)。
不能體現(xiàn)出組件的包含關(guān)系,也不能利用繼承和多態(tài)等面向?qū)ο蟮奶匦允钩绦蚋菀拙帉?xiě)和理解。
對(duì)于部分具體關(guān)聯(lián)關(guān)系的組件在處理上略顯麻煩,沒(méi)有合理的事件通知機(jī)制。
我們來(lái)看看第一條:關(guān)于參數(shù)的傳遞,在許多場(chǎng)景下對(duì)于多個(gè)條目的列表,對(duì)應(yīng)每一個(gè)條目我們一般會(huì)給元素分配一個(gè)唯一一的id,這些元素的行為類似,不同之處只是服務(wù)器端的編號(hào)不同,比如一個(gè)留言列表或者是一個(gè)產(chǎn)口列表。我們可以利用id屬性為我們作一些事情,看下面的代碼,我們用id屬性把條目對(duì)應(yīng)的服務(wù)器端編號(hào)告訴javascript,并在接下來(lái)的事件邏輯處理中作為服務(wù)器回調(diào)函數(shù)參數(shù)的一部分發(fā)回服務(wù)器端。
<script type="text/javascript">
Yottaa.init_sampleajax = function(obj){
$(obj).click(function(){
var component_id = $(this).attr('id').split('-')[1];
$.get('/server/controller/method', {id: component_id}, function(data){
if(data){
alert('Message from server: ' + data );
}
});
});
}
</script>
<a href="javascript:;" class='j a_sampleajax' id='item-a'>Show server message. </a>
<a href="javascript:;" class='j a_sampleajax' id="item-b">Another button with same action but different server side identifier.</a>
在更復(fù)雜的一些場(chǎng)景中我們可以利用頁(yè)面上的inline code給組件傳遞一些必要的信息。
Yottaa.globalConst = {
User:{
familyName: "Jhone",
givenName: 'bruce'
},
Url:{
siteName: 'yottaa.com',
score: 98
}
}
Yottaa.componentMetaData = {
compoment_id_1:{ ...... },
component_id_2:{ ...... }
};
上面討論了一種可能的代碼組織辦法,但是并非適用于所有的項(xiàng)目,我們要做的是:針對(duì)于目前的現(xiàn)狀,找到一個(gè)在代價(jià)比較小的重構(gòu)方案。我們考慮如下幾點(diǎn):
分離元素的事件綁定代碼和組件代碼:組件代碼包括jquery庫(kù),相關(guān)擴(kuò)展插件,以及我們自己編寫(xiě)的小部件,如chartbox等內(nèi)容。
事件綁定及處理邏輯:按不同的組件劃分為多個(gè)模塊,每個(gè)模塊放入一個(gè)function中。
頁(yè)面需要指定哪些模塊要在本頁(yè)面上初始化,提供一個(gè)列表交由全局的事件綁定器統(tǒng)一處理。
下面來(lái)演示一下部分代碼:
<script type="text/javascript">
function init_loginPanel = function(){
var container = $('login_panel');
$('#login_button').click(function(){
......
});
}
function init_chart = function(){
......
}
//global static init method
Yottaa.initComponents = function(components){
for(var i = 0;i<components.length;i++){
if(typeof window[components[i]] == 'Function'){
window[components[i]]();
}
}
}
// above is in the 'all-in-one' assets file which is compressed to one file in production.
var components = ['init_loginPanel', 'init_chart'];
var metaData = {
loginPanel: {},
chart: {},
......
};
$(function(){
Yottaa.initComponents(components);
});
//here is inline script on the page.
</script>
"$"函數(shù)將在文檔加載完成之后為一個(gè)指定的button 綁定事件,這些代碼在單個(gè)網(wǎng)頁(yè)中工作正常。但是如果我們還有其它的網(wǎng)頁(yè),我們將不得不重復(fù)這個(gè)過(guò)程。
復(fù)制代碼 代碼如下:
<a href="javascript:;" id="sayHello">Say Hello</a>
<script type="text/javascript">
//when dom ready, do something.
//bind click event to a button.
$(function(){
$('#sayHello').click(function(){
alert('Hello world!');
});
});
</script>
如果我們需要另一個(gè)行為的button怎么辦?比如象這樣:
復(fù)制代碼 代碼如下:
<a href="javascript:;" id="sayUnlike">Unlike it</a>
<script type="text/javascript">
//when dom ready, do something.
//bind click event to a button.
$(function(){
$('#sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
接下來(lái),更多的問(wèn)題出現(xiàn)了,我們需要很多這樣的button, 這好象也不難。
復(fù)制代碼 代碼如下:
<a href="javascript:;" class="sayUnlike">Unlike it</a>
<script type="text/javascript">
//Change to a class selector to match all the button elements.
$(function(){
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
一個(gè)頁(yè)面里面同種出現(xiàn)了兩種button ......
復(fù)制代碼 代碼如下:
<a href="javascript:;" class='sayHello'>Say Hello</a>
<a href="javascript:;" class="sayUnlike">Unlike it</a>
<script type="text/javascript">
$(function(){
$('.sayHello').click(function(){
alert('Hello world!');
});
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
但是呢,不是所有的頁(yè)面都會(huì)用到這兩種的button,為了不在頁(yè)面上使用額外的選擇器,我們要作一些必要的調(diào)整,因?yàn)榛赾lass的選擇器的性能相對(duì)于id選擇器開(kāi)銷很大,需要遍歷所有dom元素,并使用正則表達(dá)式匹配class屬性來(lái)選定滿足條件的元素。
復(fù)制代碼 代碼如下:
<? if($page == 'A'){?>
<script type="text/javascript">
$(function(){
$('.sayHello').click(function(){
alert('Hello world!');
});
});
</script>
<? } ?>
<? if($page == 'B'){?>
<script type="text/javascript">
$(function(){
$('.sayUnlike').click(function(){
alert('I unlike it.');
});
});
</script>
<? } ?>
我們的項(xiàng)目功能越來(lái)越復(fù)雜,經(jīng)過(guò)一段時(shí)間以后,變成了這個(gè)樣子, quick but dirty......
復(fù)制代碼 代碼如下:
<? if($page == 'A' or $page == "C" and $page is not "D"){ ?>
<script type="text/javascript">
......
</script>
<? } ?>
<? if($page == "B" or $page == "E" and $page is not "X"){ ?>
<script type="text/javascript">
.....
</script>
<? } ?>
<? if($page == "B" or $page == "E" or $page == "C"){ ?>
<script type="text/javascript">
.....
</script>
<? } ?>
這真是太糟糕了,我們需要在一個(gè)頁(yè)面上加載許多個(gè)代碼片斷才能綁定所有的事件,如果我們?cè)賹⒉煌拇a分裝入多個(gè)js文件中這將增加多個(gè)頁(yè)面資源的http請(qǐng)求,不論是管理還是用戶體驗(yàn)都將面臨挑戰(zhàn),我們需要找到一個(gè)更佳的解決方案。
既然 class selector 的開(kāi)銷這么大,我們能不能在一次掃描中綁定所有的事件?我們可以嘗試一下:
復(fù)制代碼 代碼如下:
<script type="text/javascript">
//Register global name space.
var Yottaa = Yottaa || {};
Yottaa.EventMonitor = function(){
this.listeners = {};
}
//Bind all event.
Yottaa.EventMonitor.prototype.subscribe=function(msg, callback){
var lst = this.listeners[msg];
if (lst) {
lst.push(callback);
} else {
this.listeners[msg] = [callback];
}
}
// Create the event monitor instance.
var event_monitor = new Yottaa.EventMonitor();
function load_event_monitor(root){
var re = /a_(\w+)/; //using a regular expression to filter all event object.
var fns = {};
$(".j", root).each(function(i) {
var m = re.exec(this.className);
if (m) {
var f = fns[m[1]];
if (!f) { //如果事件處理函數(shù)不存在則創(chuàng)建函數(shù)對(duì)象.
f = eval("Yottaa.init_"+m[1]);
fns[m[1]] = f;//調(diào)用綁定函數(shù).
}
f && f(this);
}
});
}
$(function(){
// when dom ready, bind all event.
load_event_monitor(document);
});
//Here is 2 sample components.
Yottaa.init_sayhello = function(obj){
$(obj).click(function(){
alert('Hello world!');
});
}
Yottaa.init_unlike = function(obj){
$(obj).click(function(){
alert('I unlike it.');
});
}
</script>
我們的DOM元素這樣寫(xiě):
<a href="javascript:;" class="j a_sayhello">Say Hello</a>
<a href="javascript:;" class="j a_unlike">Say Unlike</a>
這樣看起似乎好多了,我們只需要在頁(yè)面加載的時(shí)候執(zhí)行一次class selector(在上面的代碼中就是所有'.j'的元素)就可以找到所有需要綁定事件的元素,具體綁定哪一個(gè)組件由 class 名稱里面的 a_xxx 來(lái)決定,對(duì)應(yīng)著 Yottaa.init_xxx,并將當(dāng)前元素的引用作為參數(shù)傳入事件邏輯中。
在這個(gè)處理模式下,我們不需要再次手動(dòng)編寫(xiě)事件處理的邏輯并將它放到 $(function(){ .... }); 這樣的初始化函數(shù)中,所有我們要做的事情僅僅是給組件的“容器”加上兩個(gè) class: "j a_XXX"程序即可幫我完成事件綁定工作,是不是很 cool ?象常用的展開(kāi)/折疊效果,全選/反選效果, tab切換以致于一些其它的簡(jiǎn)單功能都可以使用這種方式。難道這就是傳說(shuō)中的銀彈?不,事情沒(méi)那么簡(jiǎn)單,我們應(yīng)該看到這種處理方式一些弱點(diǎn):
不能給組件傳遞初始化參數(shù)。
不能體現(xiàn)出組件的包含關(guān)系,也不能利用繼承和多態(tài)等面向?qū)ο蟮奶匦允钩绦蚋菀拙帉?xiě)和理解。
對(duì)于部分具體關(guān)聯(lián)關(guān)系的組件在處理上略顯麻煩,沒(méi)有合理的事件通知機(jī)制。
我們來(lái)看看第一條:關(guān)于參數(shù)的傳遞,在許多場(chǎng)景下對(duì)于多個(gè)條目的列表,對(duì)應(yīng)每一個(gè)條目我們一般會(huì)給元素分配一個(gè)唯一一的id,這些元素的行為類似,不同之處只是服務(wù)器端的編號(hào)不同,比如一個(gè)留言列表或者是一個(gè)產(chǎn)口列表。我們可以利用id屬性為我們作一些事情,看下面的代碼,我們用id屬性把條目對(duì)應(yīng)的服務(wù)器端編號(hào)告訴javascript,并在接下來(lái)的事件邏輯處理中作為服務(wù)器回調(diào)函數(shù)參數(shù)的一部分發(fā)回服務(wù)器端。
復(fù)制代碼 代碼如下:
<script type="text/javascript">
Yottaa.init_sampleajax = function(obj){
$(obj).click(function(){
var component_id = $(this).attr('id').split('-')[1];
$.get('/server/controller/method', {id: component_id}, function(data){
if(data){
alert('Message from server: ' + data );
}
});
});
}
</script>
<a href="javascript:;" class='j a_sampleajax' id='item-a'>Show server message. </a>
<a href="javascript:;" class='j a_sampleajax' id="item-b">Another button with same action but different server side identifier.</a>
在更復(fù)雜的一些場(chǎng)景中我們可以利用頁(yè)面上的inline code給組件傳遞一些必要的信息。
復(fù)制代碼 代碼如下:
Yottaa.globalConst = {
User:{
familyName: "Jhone",
givenName: 'bruce'
},
Url:{
siteName: 'yottaa.com',
score: 98
}
}
Yottaa.componentMetaData = {
compoment_id_1:{ ...... },
component_id_2:{ ...... }
};
上面討論了一種可能的代碼組織辦法,但是并非適用于所有的項(xiàng)目,我們要做的是:針對(duì)于目前的現(xiàn)狀,找到一個(gè)在代價(jià)比較小的重構(gòu)方案。我們考慮如下幾點(diǎn):
分離元素的事件綁定代碼和組件代碼:組件代碼包括jquery庫(kù),相關(guān)擴(kuò)展插件,以及我們自己編寫(xiě)的小部件,如chartbox等內(nèi)容。
事件綁定及處理邏輯:按不同的組件劃分為多個(gè)模塊,每個(gè)模塊放入一個(gè)function中。
頁(yè)面需要指定哪些模塊要在本頁(yè)面上初始化,提供一個(gè)列表交由全局的事件綁定器統(tǒng)一處理。
下面來(lái)演示一下部分代碼:
復(fù)制代碼 代碼如下:
<script type="text/javascript">
function init_loginPanel = function(){
var container = $('login_panel');
$('#login_button').click(function(){
......
});
}
function init_chart = function(){
......
}
//global static init method
Yottaa.initComponents = function(components){
for(var i = 0;i<components.length;i++){
if(typeof window[components[i]] == 'Function'){
window[components[i]]();
}
}
}
// above is in the 'all-in-one' assets file which is compressed to one file in production.
var components = ['init_loginPanel', 'init_chart'];
var metaData = {
loginPanel: {},
chart: {},
......
};
$(function(){
Yottaa.initComponents(components);
});
//here is inline script on the page.
</script>
相關(guān)文章
JavaScript中指定函數(shù)名稱的相關(guān)方法
這篇文章主要介紹了JavaScript中指定函數(shù)名稱的相關(guān)方法,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06JavaScript中的數(shù)據(jù)類型轉(zhuǎn)換方法小結(jié)
這篇文章主要介紹了JavaScript中的數(shù)據(jù)類型轉(zhuǎn)換方法,是JavaScript入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10document.open() 與 document.write()
document.open() 與 document.write()...2006-10-10JavaScript基礎(chǔ)語(yǔ)法讓人疑惑的地方小結(jié)
JavaScript基礎(chǔ)語(yǔ)法讓人疑惑的地方小結(jié),學(xué)習(xí)js的朋友可以了解下2012-05-05淺談JavaScript中的字符編碼轉(zhuǎn)換問(wèn)題
這篇文章主要介紹了淺談JavaScript中的字符編碼轉(zhuǎn)換問(wèn)題,是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-07-07