jQuery的設計藝術和選擇器(二)
這個標題后跟著的(二)會比較讓人奇怪,因為在我的博客上找不到“jQuery的設計藝術和選擇器(一)”這樣的文章。
這篇文章的同系列上一篇文章是從擴展方法到流暢的程序體驗(一),這是Jumony引擎的介紹系列文章第二篇。
jQuery是一個了不起的輕量級的JavaScript框架,事實上在jQuery發布之前,就已經有無數功能強大得多的JavaScript框架在流行。從功能列表上來說,jQuery在JavaScript框架中只能算是很不起眼的小弟。但這個小弟在短短的時間內,就成為了最流行的JavaScript框架之一。當然jQuery成功的原因有很多,跨瀏覽器兼容、輕巧、不算很差的性能,以及jQuery的“口號”:write less, do more。
有人會說,write less, do more就是jQuery的全部設計哲學,但我不這么認為。
在我看來,write less, do more是任何一個框架都必須去做的事情。很難想象會有人選擇一個write more, do less的框架吧。所以,對于一個框架而言,這是最基本的事情,要成為一個偉大的框架,顯然這很不夠。
在這里我想談談jQuery的一個設計的藝術,select then do。
CSS選擇器是jQuery最重要的函數$(或者說jQuery)最常見的參數。盡管這個函數可以接受的參數還有HTML元素或是HTML代碼。但最常用的顯然是傳入一個CSS選擇器,jQuery會幫你選擇文檔中符合要求的元素。然后,你就可以對這個元素集進行操作:
這段腳本可以將所有li元素設置為斜體。
這是jQuery最常見的用法,利用選擇器選擇自己所需的元素,然后對其執行某些操作。為了方便進行多個操作,jQuery絕大多數的函數都返回執行函數的原對象,在這里也就是$("li")。所以我們可以簡單的繼續寫:
這里面透露出來了一個jQuery設計的哲學,即select then do,select就是選擇所需的元素,do就是執行某些操作。絕大多數時候,我們都是select.do.do.select.do.do.do,比如說:
show().find("a")
.text("刪除")
.attr("href", "javascript:void(0);")
.click(function () { $(this).parent().hide(); });
很明顯的,$("li")在select然后接著兩個do,再然后.find("a"),這里在進一步進行select,然后繼續執行一系列的do。
注意在click綁定的事件處理函數里:$(this).parent().hide()也是一個非常經典的select.select.do。
這非常接近我們的自然語言,比如說上面那一段腳本,其實是這個意思:
“所有的li元素聽好了,把你們的字體搞成斜的,再給我顯示出來,然后看看你們后代里面有沒有a元素,讓它把顯示文字變成“刪除”,再把href屬性設置為"javascript:void(0);",最后他們被點擊的時候,把他們的父親隱藏掉。”
是的,我完全是照著腳本直接就可以說出來,不需要任何思考和變換。接下來,我們來看看一個糟糕的例子:
for (var i = 0; i < items.length; i++)
{
var li = items.item(i);
li.style.fontStyle = "italic";
li.style.display = "";
var childs = li.childNodes;
for (var j = 0; j < childs.length; j++)
{
var a = childs[j];
if (a.tagName != "A")
continue;
a.innerText = "刪除";
a.href = "javascript:void(0);";
a.onclick = function ()
{
this.parentNode.style.display = "none";
};
}
}
很難想象這段腳本只是完成了相同的事情。
照著這段腳本你能簡單的描述它是干什么的么?
當然,select then do并不僅僅只是幫我們節省了代碼。更大的優勢在于,它使得我們可以將我們的邏輯和HTML文檔徹底的分開。簡單的說,在Web開發中,我們經常會遇到這樣的需求,按下一個按鈕,彈出一個選擇框讓用戶決定是否提交表單,傳統的方式是這樣:
onclick="if ( confirm( '您確定要提交這些信息么' ) )
document.getElementById('registerForm').submit();" />
顯然這很糟糕,他將行為和HTML元素死死的捆在了一起,如果我們希望這個按鈕同時干兩件事情,那真是一件災難。
jQuery的選擇器可以很好的幫助我們分離我們的行為,select then do:
if (confirm('您確定要提交這些信息么'))
$('#registerForm').submit();
});
如果是要處理復雜的事情,這會更愜意。
OK,其實到現在并沒有什么神奇的事情發生,沒有jQuery我們也可以通過DOM提供的方法簡單的通過腳本進行事件的注冊,而不是直接寫在HTML里面。只是我們要處理一下不同的瀏覽器之間的差異而已。
我們來考慮另一個場景。譬如說在頁面上有一個登陸的小區域,里面有三個輸入框,用戶名、密碼和驗證碼,然后有一個登陸按鈕,像這樣:
<table border="0" cellpadding="5" cellspacing="0">
<tr>
<td>用戶名:</td>
<td><input type="text" name="username" style="width: 100px;" /></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password" style="width: 100px;" /></td>
</tr>
<tr>
<td>驗證碼:</td>
<td><input type="text" name="validateCode" style="width: 50px;" /><img src="validateCode.img" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登陸" /></td>
</tr>
</table>
</form>
有一個很不幸的事情,這種登陸框到處都有。而你,還不得不給這個登陸框加上一些必須處理的事情,例如在提交的時候檢查一下輸入框是不是空的。
顯然我們并不希望在所有的這些頁面都去寫一小段腳本,我們希望有一段腳本,能夠自動的在有這種登陸框的頁面處理這些事情。最好是,在沒有登陸框的頁面,它也不會有任何副作用,那么這樣的腳本真的存在么?
{
var form = $("form#loginForm");
var flag = true;
form.find("input[type=text] , input[type=password]").each(function ()
{
if (this.value == "" && flag)
{
window.alert("請將登陸信息填寫完整");
flag = false;
}
});
return flag;
});
注意這段腳本中選擇器的運用,通過id限定和find方法的范圍限定,我們牢牢地將這段腳本所影響的范圍控制在了一個id為loginForm的表單中。更絕妙的是,即使這個表單不存在于頁面,這段腳本也沒有任何的問題。不會在你IE的狀態欄弄一個黃色的感嘆號告訴你腳本出現了錯誤。你可以將這段腳本大膽放心的放在每一個頁面的JavaScript的引用中(這對于現有的技術來說再簡單不過),也不用擔心明天哪個頁面多了一個登陸塊你會需要去寫什么腳本。
這就是選擇器的絕妙之處,它使得我們的頁面元素可以通過約定來獲得某些行為,例如在這里,只要我們將登陸用的表單的id設置為loginForm,那么這個表單就會自動獲得提交的時候檢查所有輸入框的行為。
這種約定的威力完全不僅如此,我們再來看一段神奇的腳本:
{
var flag = true;
$(this).find("input[type=text][required=required] , input[type=password][required=required]").each(function ()
{
if ($(this).val() == "")
{
window.alert("信息沒有填寫完整,請認真檢查必填項");
flag = false;
return false;
}
});
return flag;
});
這段神奇的腳本可以讓你只需要在你的輸入框上加一個屬性required="required",然后表單提交的時候就會自動驗證這些輸入框里面是不是填了東西。這太神奇了,我們利用jQuery提前享受了HTML5的新特性。
OK,當我意識到jQuery的選擇器如此強大的威力的時候,我馬上想到,事實上如果將選擇器運用于我們傳統的頁面數據綁定,也會是一件非常棒的事情。。。
這便是Jumony引擎的由來。Jumony將jQuery的選擇器和select then do藝術幾乎完整的搬到了C#中。在項目開發中帶來的效率提升和暢快的感覺,完全的超出了我原本的設想。
在這里,我仍不愿意過多的去談Jumony的功能細節。由于這個引擎仍在不斷的開發修改和內部測試中,現在并沒有可以公開的預覽,我只能說,敬請期待。我會繼續分享在Jumony開發和設計中過程中的,嗯,心得。
最新的Jumony build已經實現如下選擇器支持:
#identity、.class-name、[attr]、[attr=value]、[attr!=value]、[attr^=value]、[attr$=value]
:nth-child、:nth-last-child、:nth-of-type、:nth-last-of-type、
:first-child、:last-child、:first-of-type、:last-of-type
留言列表