第十二章 前端JavaScript框架:jQuery
12.1 jQuery簡介
jQuery是目前應用最爲廣泛,最爲優秀的Ajax/JavaScript開源框架之一,有數以千萬記的用戶,更有多不勝數的技術文檔與之相關,在一定程度上,jQuery如其所宣揚的那樣,改變了人們編寫JavaScript的方式。
jQuery通過提供CSS標準的選擇器來對頁面元素進行選擇,然後對這些元素組成的一個列表進行某些操作,參與過頁面開發的人員都知道,基於WEB的UI實際上要做的事情就是:
- 找到頁面上的某個/某些元素
- 改變這些元素的屬性或者樣式
- 綁定一些事件處理程序在這些元素上
爲了對開發者更友好,jQuery使用了獨特的鏈式操作,使得可以使用儘可能的代碼來完成儘可能多的任務。就個人而言,jQuery是我個人最喜歡的JavaScript框架。我們可以通過一些例子來看看jQuery是怎樣工作的。
假設我們有一個table,如果給table加上斑馬線的話,用戶可以更清晰的看清楚每一行,整個頁面也更有層次感,但是問題是我們的頁面已經做好了,我不想再對頁面做修改!
圖 修改前的表格
這樣單調的一種顏色很容易使用戶的視覺產生疲勞,我們應該爲偶數行添加淺綠色的背景,像這樣:
圖 修改後的表格
這樣效果就好多了,要做成這種效果,用jQuery需要多少代碼呢?一行!我們來看看如何用一行代碼完成這樣的效果:首先,我們找到這個table,然後告訴jQuery,給這個table中的所有偶數行都添加一個css僞類:
$("div#informationTable table tr:nth-child(even)") .addClass("striped");
“div#informationTable table tr:nth-child(even)”爲一個CSS標準的選擇器,表示在一個id爲informationTable的div的孩子中,找到所有的table,tr是table的孩子,並使行,nth-child(even)表示爲偶數的孩子。
完整的代碼如下:
$(document).ready(function(){ $("div#informationTable table tr:nth-child(even)") .addClass("striped"); });
這段代碼表示,當文檔加載完成後($(document).ready()),調用一個匿名的函數,這個函數的函數體爲我們上面分析過的,找到table的所有偶數列,爲這些列添加背景色(通過使用css類”striped”)。
事實上,jQuery深受開發人員青睞的更深層次的原因可能要歸功於貫穿於其中的編程思想,如果仔細審視jQuery的代碼,你應該會發現,其中的集合的概念以及對集合的各種操作,與函數式語言lisp是不謀而合的,比如map,filter,以及grep等等。
我們可以來看看這樣幾個簡單示例:
jQuery.grep對列表進行過濾,並返回過濾後的列表。我們來看一個例子,首先定義一個人員列表,每個條目包含name和age字段,現在要找出所有age大於24歲的人員,並以列表的形式返回:
$(document).ready(function(){ var person = [ {name : "jack", age : 26}, {name : "johb", age : 23}, {name : "smith", age : 20}, {name : "abruzzi", age : 26}, {name : "juntao", age : 25}, {name : "jim", age : 24}, {name : "bob", age : 24} ]; var gt23s = $.grep(person, function(item){ return item.age > 24; }); console.dir(gt23s); });
jQuery的工具方法定義在jQuery對象上,類似於Java中的靜態方法。console.dir是Firefox或者Chrome的調試助手,在chrome下的結果如下:
圖 chrome開發人員工具中console.dir的效果示意($.grep)
再來看一下map的例子:
$(document).ready(function(){ var person = [ {name : "jack", age : 26}, {name : "johb", age : 23}, {name : "smith", age : 20}, {name : "abruzzi", age : 26}, {name : "juntao", age : 25}, {name : "jim", age : 24}, {name : "bob", age : 24} ]; var mapped = $.map(person, function(item){ return item.name = item.name.toUpperCase(); }); console.dir(person);//原始的person列表已被修改 });
我們將person列表中的每一個元素的name字段的值轉換爲大寫。Map的修改是直接體現在原始列表上的,結果如下:
圖 chrome開發人員工具中console.dir的效果示意($.map)
jQuery本身不如ExtJs那樣可以輕鬆而快速的開發出華麗的UI,但是jQuery本身提供的插件機制爲使用jQuery方式快速開發華麗的UI提供了可能,比如jQuery-UI, EasyUI等插件的出現,使得用戶可以向使用jQuery那樣,快速的生成UI,提高開發速度。
12.2 jQuery基礎
12.2.1 jQuery選擇器
jQuery最強大易用的即是它提供的選擇器,它支持CSS選擇器及其擴展,很方便已經熟悉傳統web開發模式的用戶快速過渡到jQuery上來。比如下面這些常用的CSS選擇器:
- div.highlight 選擇CSS類highlight的所有div元素
- table#tabid 選擇ID爲tabid的table元素
- a#aid.aclass 選擇ID爲aid,CSS類爲aclass的鏈接元素
這些選擇器均可直接在jQuery中使用,只需要將選擇器包裝在$()中即可。jQuery的選擇器完全兼容CSS3選擇器。這爲跨瀏覽器的web應用提供了極大的便利。
除了這些基本的CSS選擇器外,jQuery提供了更豐富的選擇器,如通過位置選擇:
選擇器 |
作用 |
:first |
選擇第一個匹配 |
:last |
選擇最後一個匹配 |
:first-child |
選擇第一個字元素 |
:last-child |
選擇最後一個子元素 |
:nth-child(n) |
選擇第n個子元素 |
:even 及 :odd |
選擇偶數/奇數子元素集 |
:eq(n) |
第n個元素(從0開始) |
:gt(n) |
選擇第n個元素之後的元素集 |
:lt(n) |
選擇第n個元素之前的元素集 |
比如:
- li a:first 匹配在列表(<li>)元素下的第一個鏈接(<a>)元素。
- table tbody td:nth-child(5)返回table的第6列元素集。
自定義選擇器:
選擇器 |
作用 |
:button |
選擇按鈕 |
:checkbox |
選擇複選框 |
:checked |
選擇已經選中的複選框/單選框 |
:hidden |
選擇屬性爲隱藏的元素 |
:enable |
選擇啓用的元素 |
:disable |
選擇禁用的元素 |
:image |
選擇圖片 |
:input |
選擇輸入框 |
:radio |
選擇單選框 |
:not(filter) |
反向選擇器 |
應該注意的是,這些選擇器可以組合使用,這樣會給我們提供極大的方便,比如:
- :input:enable 選擇已經啓用的文本框元素
- :checkbox:checked 選擇已經選擇的複選框元素
12.2.2對DOM的操作
在實際應用中,經常需要操作DOM元素,比如插入一段HTML到指定位置,刪除某些被選擇的DOM段,修改某些元素的內容等。
例如有一個HTML頁面:
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css" />
<script type="text/javascript" src="jquery-1.3.2.js"></script>
<script type="text/javascript" src="selector.js"></script>
</head>
<body>
<div id="container"></div>
</body>
</html>
樣式表爲:
div#container{ background:blue; border:1px solid black; width:200px; height:200px; } div#child{ background:yellow; border:1px solid black; width:100px; height:100px; }
container是一個藍色的200x200的方框,我們要動態的爲這個div添加一個子元素,子元素的ID爲child:
$(function(){ var container = $("div#container"); $("<div id='child'></div>").appendTo(container); });
運行結果如上圖所示。
再進一步,我們爲頁面添加一個按鈕(clean),點擊此按鈕將清除新添加的黃色child方框。
<input type="button" id="clean" value="clean" />
並添加JavaScript代碼:
$(function(){ var container = $("div#container"); $("<div id='child'></div>").appendTo(container); $("input#clean").click(function(){ container.find("#child").remove(); }); });
單擊clean按鈕之後,將會移除新添加的child框。
12.2.3對CSS的操作
使用jQuery,可以很方便的對CSS類進行添加/刪除/toggle等操作,我們來看一個簡單的示例:
首先定義三個CSS類:base,red-region,yellow-region:
.base{ background:white; border:1px solid black; width:200px; height:200px; } .red-region{ background:red; border:1px solid blue; width:200px; height:200px; } .yellow-region{ background:yellow; border:1px solid green; width:200px; height:200px; }
定義一個ID爲base的面板,兩個按鈕:red和yellow。當點擊red時,判斷base是否已經被yellow修飾過,如果已經被修飾過,則移除CSS類yellow-region。點擊yellow時情形類似:
var base = $("div#base"); $("input#red").click(function(){ if(base.hasClass("yellow-region")){ base.removeClass("yellow-region"); } $("div#base").addClass("red-region"); }); $("input#yellow").click(function(){ if(base.hasClass("red-region")){ base.removeClass("red-region"); } $("div#base").addClass("yellow-region"); });
頁面效果如下:
點擊red按鈕之後,base添加了red-region的CSS類,變爲紅色:
在使用jQuery選擇器選擇到預期的元素集之後,我們可以修改器CSS,來完成頁面的動態化。動態修改CSS非常簡單:
var base = $("div#base"); base.css('width', '300px'); base.css({ 'width' : '300px', 'height' : '300px', 'background' : 'green' });
使用css函數,可以進行一個值的修改,同樣可以傳入一個集合,整體進行修改。
12.2.4事件處理
事實上,在上邊的例子中很多地方已經涉及到jQuery事件處理部分了。jQuery不但提供基本的bind/unbind來負責註冊及刪除事件處理器,同時還提供很多更方便web開發的的助手函數,如toggle/hover等。
註冊一個事件處理器非常容易:
var base = $("div#base"); base.bind('click', function(event){ alert($(this).width()+", "+$(this).height()); });
當鼠標單擊ID爲base的div時,觸發該事件。使用unbind將事件處理器刪除。我們來看一個小例子:
單擊bind按鈕時,我們爲按鈕上方的div註冊click事件處理器,點擊unbind時,移除該事件處理器:
$("input#bind").click(function(){ base.bind('click', function(event){ alert($(this).width()+", "+$(this).height()); }); }); $("input#unbind").click(function(){ base.unbind('click'); });
這樣,點擊bind之後,點擊div則會彈出一個對話框:
點擊unbind之後,div將不會再處理click事件。有時候,我們會需要爲單擊的次數爲奇數和偶數時註冊不同的事件處理器,如第一次單擊時將panel的背景色變爲紅色,再次單擊則將背景色變爲黃色,這時候我們可以使用toggle函數:
var base = $("div#base"); base.toggle( function(){ $(this).css('background', 'red'); }, function(){ $(this).css('background', 'yellow'); } );
當然,更多的是處理鼠標移入/移出事件的hover,當用戶在頁面上移動鼠標,將展現不同的視覺效果:
base.hover( function(event){ $(this).css('background', 'red'); }, function(event){ $(this).css('background', 'yellow'); } );
12.2.5實用函數
jQuery除了提供對DOM操作的API之外,還提供了操作普通JavaScript對象的一些函數,這些函數均已”$.”開頭,非常方便易用。這些實用函數包括:對字符串操作的函數,遍歷對象的函數,過濾數組中元素等。
我們來看一些小例子:
var obj = { a : 'apple', b : 'borland', c : 'cisco', d : 'dell' }; $.each(obj, function(name, value){ var li = $("<li></li>"); li.html("["+name+"]=["+value+"]"); li.appendTo(base); });
遍歷對象obj,然後將其中的鍵值對拼裝成一個字符串,添加到一個panel上:
$.grep/$.map兩個實用函數已經在第一小節做過基本的講解,這裏僅列舉出兩個函數的原型:
/** * array : 要過濾的數組對象 * callback : 過濾條件 * invert : 是否啓用反轉,如果啓用,則符合callback的將被過濾 */ $.grep(array, callback, invert); /** * array : 需要做轉換的數組對象 * callback : 對數組中元素的映射函數 */ $.map(array, callback);
有時候,我們可能需要合併數個對象爲一個對象,覆蓋其中重複的項等:
var obj1 = { name : 'juntao', last : 'qiu', }; var obj2 = { addr : 'unknown', title : 'unknown' }; var obj3 = { addr : 'KunMing, Yunnan, China' }; result = $.extend({}, obj1, obj2, obj3); $.each(result, function(name, value){ var li = $("<li></li>"); li.html("["+name+"]=["+value+"]"); li.appendTo(base); });
obj1, obj2, obj3的屬性被合併在一起,並且obj3中的addr屬性覆蓋了obj2中的addr屬性。
總而言之,jQuery是一個小巧,實用,易用且功能強大的框架。使用它,可以將原本複雜難懂的JavaScript代碼壓縮至很小,而且更容易維護,代碼更加優美。jQuery可以稱得上是web上的lisp。
12.3 jQuery實例
在這一小節,我們將使用jQuery開發一個簡單的todo管理器jqtodo。jqtodo使用httpd+php腳本作爲後臺,數據庫使用小巧的sqlite。jqtodo簡單到僅支持新建一個todo及對之前所有todo的查詢操作。
頁面佈局上,有一個輸入框和一個按鈕,用戶在輸入框中填寫待辦事項,然後點擊按鈕,即可將這條待辦事項添加到數據庫中,並同時將頁面的待辦事項列表更新:
簡單起見,這裏沒有對用戶的輸入做任何校驗,如果插入成功,則展示在列表中:
我們需要用jQuery做的事情如下:
- 當點擊add item時,將文本框中的內容取出,併發送給服務器
- 當服務器完成並響應時,我們需要及時的更新列表
- 當用戶首次進入此頁面時,需要將歷史中的待辦事項列出來
在頁面上定義一個div,其id爲itemlist,則當進入頁面時,可以通過jQuery.ajax來獲取數據庫信息,並填充頁面:
var list = $("div#itemlist"); $.ajax({ url : 'queryitems.php', type : 'GET', error : function(xhr){ alert(xhr); }, success : function(obj){ obj = eval('('+obj+')'); var dataset = obj.dataset; for(var i = 0; i < dataset.length; i++){ var current = dataset[i]; var newitem = $("<div></div>").text(current.desc) .attr({ "id" : current.itemid, "time" : current.ctime }) .addClass("item"); newitem.appendTo(list); } } });
後臺提供一個queryitems.php的頁面,該頁面負責查詢數據庫,並將結構及作爲json數組的格式返回,並將數據集存放在”dataset”屬性中,當成功時,我們可以遍歷這個數組,並動態的創建條目,爲條目添加屬性及CSS類,最後將其添加到id爲itemlist的div上展現。
類似的,頁面上有一個id爲add的按鈕,單擊該按鈕將觸發以下事件:
$("input#add").click(function(){ var item = $("input#item").val(); if(!item || item.length == 0){ alert("please set the item description"); return false; }else{ additem(item); } });
首先獲取文本框中的字符串,並做一下簡單的非空校驗。通過校驗後則調用additem函數進行查詢及頁面更新:
function additem(item){ var dat = "item="+item; $.ajax({ url : 'additem.php', type : 'POST', dataType : 'text', data : dat, error : function(xhr){ alert(xhr); }, success : function(obj){ obj = eval('('+obj+')'); var newitem = $("<div></div>").text(obj.desc) .attr({ "time" : obj.ctime }) .addClass("item"); newitem.appendTo(list); } }); }
在這個函數中,只是簡單的組織了需要POST的數據,然後使用jQuery.ajax異步的更新頁面中的待辦事項列表。
使用jQuery,可以在很短小的代碼量中完成很多工作,一般而言,簡潔的代碼更容易維護和擴展。哪怕僅僅只是從代碼的可讀性和美學的意義上來講,jQuery也非常值得一試。
附:由於作者本身水平有限,文中難免有紕漏錯誤等,或者語言本身有不妥當之處,歡迎及時 指正,提出建議,參與討論,謝謝大家!
另:此係列從發佈之初,就一直引名稱而引起朋友們的質疑,因此決定修改爲《JavaScript核心及實踐》,不知道各位朋友有何建議?謝謝!
又:此係列的後續章節也基本就緒,主要討論其他應用程序及服務器端的JavaScript的使用及技巧,新的章節可能會在做着新的博客I Code It(http://www.icodeit.org/)中發表,各位可以關注一下,這裏也算是爲新博客做個小廣告,呵呵。