10/10/2018, 11:08
[Advanced] Viết plugin cho jQuery - Nâng cao
[NẾU CÓ DÙNG BÀI VIẾT, XIN BẠN TÔN TRỌNG QUYỀN TÁC GIẢ BẰNG CÁCH GIỮ NGUYÊN DẤU TÁC QUYỀN [x] BÊN DƯỚI BÀI VÀ DÒNG CHỮ NÀY, THÂN]
Ngồi rảnh không có gì làm nên ngồi viết cái này cho những bạn nào có kiến thức cơ bản về javascript và muốn nâng cao hơn đọc. Đánh giá mức độ của mình cho bài viết này là Khó.
Giả sử rằng các bạn đã nắm vững những hàm cơ bản của mảng, đối tượng,... Và nắm vững ở đây có nghĩa là nhìn vào là biết, ko phải lật sách hay google nó nhá.
Đầu tiên, như mọi ứng dụng jQuery khác, bạn phải nhập vào thư viện jQuery:
Khởi tạo 1 plug in jQuery? Việc này không khó và được xem như là cơ bản cần phải biết:
Giải thích: Đây là hàm không tên, có cấu trúc dạng (function(){})() được gọi và thực thi ngay sau khi viết [nhờ vào cái () ở phía cuối á], trong hàm này, nó đưa giá trị jQuery vào dưới dạng $ để có thể dùng được ký hiệu dollar quen thuộc trong jQuery.
Mục đích của bài viết này là custom 1 hàm đơn giản tên là fly() với thuộc tính và chức năng như sau:
Chà, nhức đầu chưa, nếu quan sát kĩ, thì đây chỉ là hàm bình thường, tuy nhiên sau khi bay xong, nó lại gọi 1 hàm khác để bay thêm 1 vòng nữa, và sau khi bay xong lần 2 nó thông báo hoàn tất. Chóng mặt thật xD!
Vậy thì qua ví dụ này, chắc chắn bạn sẽ nắm được:
Và đảm bảo với các bạn, trong quá trình làm, cả chục vấn đề phát sinh ra, cả chục kinh nghiệm lý thú của mình .
Ok, bắt tay vào việc:
Đầu tiên nói về đặt tên cho 1 hàm, như sau đây là một tối kiến KHÔNG NÊN LÀM:
Vì sao? Vì khi bạn đặt quá nhiều tên, khả năng xung đột giữa cái custom plugin khác và custom plug in của bạn tăng lên rất cao, có thể làm hỏng chương trình của người dùng! Vậy thì thay vì người dùng gọi:
Thì hãy để người dùng gọi theo cách này:
Để làm được cái đó, các bạn sẽ cần hàm sau [phân tích bên dưới]
Chắc chắn rằng bạn có thể hiểu được những điều trên trước khi đọc tiếp nha @@! Nếu hiểu sơ sơ cũng được, vì sau khi làm vài lần, bạn sẽ nhuyễn như ăn cháo thôi.
Chẹp, thế là xong phần cơ bản của khởi tạo 1 hàm rồi, bây giờ thử đổi hàm init xem sao:
Gọi nó nào, ý quên, còn phải tạo 1 <div> để test chứ:
Giờ thì an tâm gọi rồi, nhớ gọi nói khi document ready, điều cơ bản nhất của jQuery:
Nếu thành công, bạn sẽ nhận được thông báo "test" [vì id của đối tượng này là test mà].
Tiếp theo hãy dùng phương pháp truyền thống để truyền tham số vào cho đối tượng thử nhé:
Gọi nó nào:
Kết quả là gì, thất bại. Hahaha, mình đưa ra ví dụ này để cho thấy rằng, nếu bạn truyền tham số "HELLO" vào hàm fly(), nó sẽ không nghĩ đây là tham số, mà sẽ tưởng nhầm là 1 method [phần trên], tức là nó sẽ gọi methods["HELLO"] và kết quả trả về là method ko tồn tại.
Vậy thì làm sao? Đây là nơi mình phải truyền tham số bằng cách dùng mảng rồi. Nói về cách dùng mảng, từ lúc này trở đi, bạn phải nhuyễn như ăn cháo cách viết:
Thay vì cách thông thường:
Cách mới ngắn hơn, và dễ quản lý hơn rất nhiều. Viết code theo mình là hướng đến tính đơn giản và đa chức năng. Vậy bây giờ hãy truyền tham số vào cho nó nhé:
Gọi nó nào:
Kết quả trả về là "My is is test", đúng ko? Vậy là đã thành công rồi đó. Tuy nhiên đến lúc này vẫn chưa phải là phần khó lắm.
Nếu bạn có kiên nhẫn đọc được tới đây rồi thì chúc mừng bạn, bạn sắp học được một trong những kinh nghiệm của riêng mình rồi đó.
Ok, trở lại vấn đề, do mình gọi hàm fly bằng cách dùng id ("#test"):
Nên chỉ có 1 đối tượng được gọi, vậy chuyện gì sẽ xảy ra nếu mình gọi theo class (".test"). Chà, có hàng chục đối tượng có cùng lớp như vậy, vậy nếu ở hàm init, nó chỉ thông báo 1 lần, thế giả mình muốn thực hiện với tất cả cách đối tượng khác thì sao, đến lúc này, bạn phải bố sung thêm 1 đoạn nhỏ vào cho code của mình:
Bây giờ thử tạo nhiều thẻ div với id khác nhau nhưng cùng chung class test và gọi xem, kết quả là gì nhá .
Phù. Vậy là phần sườn đã xong, bây giờ sẽ tới phần hơi phúc tạp 1 tí: Đặt mặc định cho tham số. Nói cho dễ hiểu, nhìn lại hàm trên, có phải nó sẽ thông báo hộp thoại alert(parameters["message"]+this.id) không? Vậy chuyện gì nếu parameters["message"] bị bỏ trống? Undefined? Trong ví dụ này, nó không có gì là quan trọng, tuy nhiên với những ứng dụng sau này, giả sử bạn cần màu sắc chẳng hạn, mà tham số đó ko có thì sao. Đặt mặc định chứ sao. Vậy cách làm là gì? Cách làm cơ bản nhất mà mình vẫn làm khi tự nghiên cứu:
Cách làm trên thế nào. Đúng chứ sao, 100% đúng, nhưng chuyện gì xảy ra nếu như mục đính của bạn [cũng như mục đích trong bài tut này] là dùng tới tận 4 tham số [precss, css, time, callback]. Chuyện gì sẽ xảy ra nếu số lượng tham số lên tới cả chục, hoặc cả trăm @@! Chà, vấn đề đấy. Vào một ngày đẹp trời, mình lang thang lên mạng đọc về jQuery, và cuối cùng mình đã tìm ra được cách: dùng jQuery extend().
jQuery.extend() có rất nhiều cách dùng, mình sẽ không giới thiệu từng cách 1 [các bạn nên tự tìm hiểu], mình chỉ giới thiệu 1 cách dùng để đặt mặc định thôi. Hãy nhìn vào đoạn mã sau:
Sau khi extend, parameters vẫn sẽ trả về dưới dạng 1 mảng. NHỚ CHO KỸ CÁI NÀY NHA!
Vậy là an toàn rồi nha, bây giờ không sợ có tham số rỗng nữa rồi. Quên cái ví dụ alert() đơn giản đó đi, đến lúc ứng dụng nó vào plugin thực sự rồi. Bước tiếp theo là tạo cho nó mấy giá trị mặc định như đã nói [precss, css, time và callback]:
Như bạn nhìn thấy, precss là 1 mảng rỗng [mình dùng mảng để chứa được nhiều dữ liệu hơn], css cũng là 1 mảng rỗng, time có giá trị mặc định là 2000, callback là 1 hàm rỗng. Tất nhiên bạn có thể sửa nó tùy ý bạn.
Chuẩn bị cho khái niệm trừu tượng tiếp theo chưa? Đó là sự khác nhau giữa "this" và "$(this)", tuy chúng cùng chỉ về đối tượng được chọn. Hãy tạm gác tut lại một chút và nhìn vào ví dụ dưới đây:
Bạn dễ dàng thấy được khi cần id, thì mình dùng this.id, và khi muốn động chạm đến jQuery.attr() thì mình lại phải dùng $(this). Thực ra, sự khác nhau của this và $(this) là ở chỗ $(). $() là một hàm của jQuery dùng để biến đối tượng this thành 1 object, vì chỉ có object mới có thể dùng được .attr(). Do biến thành object, nên nó không còn thuộc tính .id như ban đầu nữa. Tóm gọn lại, để dùng những hàm của jQuery, phải biến nó thành object thông qua hàm $() hay jQuery(), object là một đối tượng mới, không có bất cứ thuộc tính nào. Còn nếu để nguyên this, nó chỉ về đối tượng và giữ nguyên tất cả các thuộc tính của nó [như src, id,...] và không dùng được trong các hàm của jQuery.
Quay lại tut, bây giờ vẫn đoạn code trên, nhưng mình sẽ thêm vài dòng chú thich vào để các bạn có thể theo dõi mình đang làm gì:
Ok, điền code vào thôi, phần này khá đơn giản nên cũng không cần giải thích nhiều:
Phù, cơ bản là xong rồi, thế còn vụ callback thì sao? Làm cách nào để gọi hàm callback? Để gọi hàm callback, hãy vận dụng 1 cách gọi hàm khá cơ bản của javascript:
Chờ gì nữa, gom nó vào code, tuy nhiên, để an toàn, trước khi chạy callback function, phải chắc chắn rằng nó là 1 hàm nữa:
Xong rồi đó, nhưng nhìn kỹ lại cái đoạn tut dự định làm của mình xem, bạn có thấy cái hàm trả về ko? Nó có mảng settings để chứa các thông tin hiện tại của đối tượng, vậy nó ở đâu ra?
Rất đơn giản, thay vì truyền tham số null khi gọi hàm callback:
Thì chúng ta truyền thông tin hiện có của đối tượng trong mảng parameters xem:
Vậy là xong, đây là đoạn code cơ bản nhất và có lẽ là đầy đủ nhất về những điều cần biết khi viết 1 plugin. Bạn đã có thể gọi hàm mà ko sợ bị trùng lặp, truyền biến thông qua mảng, gọi callback, trả về đối tượng thích hợp cho hàm callback,... Hy vọng rằng tut này sẽ giúp bạn 1 phần nào trong việc bắt đầu viết 1 plug in của mình. Mình sẽ không post 1 lúc full code ở đây, vì các bạn phải đọc từng phần cho nhuyễn. Chỉ cần nối lại là xong ấy mà.
Thân.
[x] - xx3004
Bonus: Ví dụ trên, nhưng được viết dưới dạng phức tạp hơn rất nhiều, ứng dụng nhuần nhuyễn hơn những điều đã nói ở trên, mức độ KHÓ, bạn không nên đọc tiếp nếu bạn chưa nhuyễn những điều ở trên:
ENJOY
Ngồi rảnh không có gì làm nên ngồi viết cái này cho những bạn nào có kiến thức cơ bản về javascript và muốn nâng cao hơn đọc. Đánh giá mức độ của mình cho bài viết này là Khó.
Giả sử rằng các bạn đã nắm vững những hàm cơ bản của mảng, đối tượng,... Và nắm vững ở đây có nghĩa là nhìn vào là biết, ko phải lật sách hay google nó nhá.
Đầu tiên, như mọi ứng dụng jQuery khác, bạn phải nhập vào thư viện jQuery:
Code:
<script language="javascript" type="text/javascript" src="scripts/jQuery.js"></script> <script language="javascript" type="text/javascript" src="scripts/jQueryUI.js"></script>
Code:
(function($) { //Code custom jQuery nằm ở đây. })(jQuery);
Mục đích của bài viết này là custom 1 hàm đơn giản tên là fly() với thuộc tính và chức năng như sau:
Code:
//Hàm fly này sẽ làm cho đối tượng resize kích thước 600 x 600 //Và bay qua bên phải góc màn hình //Trong thời gian 2000 mili giây //Tuy nhiên trước khi thực hiện nó sẽ gán position: "absolute" để có thể bay //Và sau khi bay xong, giảm kích thước width và height đi 400 //Rồi bay ngược về góc trái màn hình trong thời gian gấp đôi ban đầu //Và xuất hiện hộp thông báo alert("Đã hoàn tất") sau khi hoàn tất bay. $("#test").fly( { precss: {position: "absolute", background: "#000000"}, css: {width: 600, height: 600, top: 0, right: 0}, time: 2000, callback: function(settings) { var w=settings["css"]["width"]; var h=settings["css"]["height"]; var t=settings["time"]; this.fly( { css: {width: w-400, height: h-400, top: 0, left: 0}, time: t*2, callback: function() { alert("Da hoan tat!"); } }); } });
Vậy thì qua ví dụ này, chắc chắn bạn sẽ nắm được:
- Cách tạo hàm cơ bản
- Gọi callback cho hàm
- Nhận giá trị biến theo dạng mảng, và có thể cài mặc định
- ...
Và đảm bảo với các bạn, trong quá trình làm, cả chục vấn đề phát sinh ra, cả chục kinh nghiệm lý thú của mình .
Ok, bắt tay vào việc:
Đầu tiên nói về đặt tên cho 1 hàm, như sau đây là một tối kiến KHÔNG NÊN LÀM:
Code:
(function($) { $.fn.fly=function(){}; $.fn.flyAgain=function(){}; $.fn.flyAgainAgain=function(){}; })(jQuery);
Code:
$("#test").fly(); $("#test").flyAgain(); $("#test").flyAgainAgain();
Code:
$("#test").fly(); $("#test").fly("again"); $("#test").fly("againAgain");
Code:
(function($) { methods=//Khởi tạo mảng chứa các hàm { init: function() { //Code for $("selector").fly() }, doSomething: function() { //Code for $("selector").fly("doSomething") } } $.fn.fly=function(m)//Khởi tạo hàm khi gọi $("selector").fly, nhận vào giá trị m { if(methods[m])//Nếu hàm methods[m] tồn tại (phía trên) {//Thực thi hàm cùng với tất cả các tham số return methods[m].apply(this,Array.prototype.slice.call(arguments,1)); } else if(typeof m=="object"||!m)//Nếu như không có gì truyền vào {//Thực thi methods["init"] return methods.init.apply(this,arguments); } else//Khác {//Thông báo hàm không tồn tại alert("Method "+m+" does not exist!"); } } })(jQuery);
Chẹp, thế là xong phần cơ bản của khởi tạo 1 hàm rồi, bây giờ thử đổi hàm init xem sao:
Code:
methods= { init: function() { alert(this.id); } }
Code:
<div id="test"></div>
Code:
$(document).ready(function() { $("#test").fly(); }
Tiếp theo hãy dùng phương pháp truyền thống để truyền tham số vào cho đối tượng thử nhé:
Code:
methods= { init: function(parameter) { alert(parameter); } }
Code:
$(document).ready(function() { $("#test").fly("HELLO"); }
Vậy thì làm sao? Đây là nơi mình phải truyền tham số bằng cách dùng mảng rồi. Nói về cách dùng mảng, từ lúc này trở đi, bạn phải nhuyễn như ăn cháo cách viết:
Code:
var a={name: "WHATEVER", age: 17};
Code:
var a=new Array(); a["name"]="WHATEVER"; a["age"]=17;
Code:
methods= { init: function(parameters) { //Nhớ rằng parameters lúc này là 1 mảng alert(parameters["message"]+this.id); } }
Code:
$(document).ready(function() { $("#test").fly( { message: "My id is " }); }
Nếu bạn có kiên nhẫn đọc được tới đây rồi thì chúc mừng bạn, bạn sắp học được một trong những kinh nghiệm của riêng mình rồi đó.
Ok, trở lại vấn đề, do mình gọi hàm fly bằng cách dùng id ("#test"):
Code:
$(document).ready(function() { $("#test").fly( { message: "My id is " }); }
Code:
methods= { init: function(parameters) { this.each(function()//Hàm này dùng để chạy lần lượt theo tất cả các phần tử được chọn { alert(parameters["message"]+this.id); }); } }
Phù. Vậy là phần sườn đã xong, bây giờ sẽ tới phần hơi phúc tạp 1 tí: Đặt mặc định cho tham số. Nói cho dễ hiểu, nhìn lại hàm trên, có phải nó sẽ thông báo hộp thoại alert(parameters["message"]+this.id) không? Vậy chuyện gì nếu parameters["message"] bị bỏ trống? Undefined? Trong ví dụ này, nó không có gì là quan trọng, tuy nhiên với những ứng dụng sau này, giả sử bạn cần màu sắc chẳng hạn, mà tham số đó ko có thì sao. Đặt mặc định chứ sao. Vậy cách làm là gì? Cách làm cơ bản nhất mà mình vẫn làm khi tự nghiên cứu:
Code:
var message=(!parameters["message"]) ? "Mặc định " : parameters["message"]; //Hay if(!parameters["message"]) {message="Mặc định ";} else {message=parameters["message"];}
jQuery.extend() có rất nhiều cách dùng, mình sẽ không giới thiệu từng cách 1 [các bạn nên tự tìm hiểu], mình chỉ giới thiệu 1 cách dùng để đặt mặc định thôi. Hãy nhìn vào đoạn mã sau:
Code:
init: function(parameters) { //Nếu message không tồn tại, thì trộn nó với message mặc định, nếu tồn tại thì thôi. var defaults={message: "Mặc định "}; parameters=$.extend({}, defaults, parameters); this.each(function() { alert(parameters["message"]+this.id); }); }
Vậy là an toàn rồi nha, bây giờ không sợ có tham số rỗng nữa rồi. Quên cái ví dụ alert() đơn giản đó đi, đến lúc ứng dụng nó vào plugin thực sự rồi. Bước tiếp theo là tạo cho nó mấy giá trị mặc định như đã nói [precss, css, time và callback]:
Code:
init: function(parameters) { var defaults={precss: {}, css: {}, time: 2000, callback: function(){}}; parameters=$.extend({}, defaults, parameters); this.each(function() { //Code sẽ nằm ở đây }); }
Chuẩn bị cho khái niệm trừu tượng tiếp theo chưa? Đó là sự khác nhau giữa "this" và "$(this)", tuy chúng cùng chỉ về đối tượng được chọn. Hãy tạm gác tut lại một chút và nhìn vào ví dụ dưới đây:
Code:
init: function(parameters) { this.each(function() { alert(this.id);//Dùng this.id nha alert($(this).attr("id"));//Thế tại sao ở đây lại dùng $(this)???? }); }
Quay lại tut, bây giờ vẫn đoạn code trên, nhưng mình sẽ thêm vài dòng chú thich vào để các bạn có thể theo dõi mình đang làm gì:
Code:
init: function(parameters) { var defaults={precss: {}, css: {}, time: 2000, callback: function(){}}; parameters=$.extend({}, defaults, parameters); this.each(function() { //Đặt tên 1 biến để dễ tham chiếu nào //Đầu tiên đặt css cho đối tượng, lấy thông tin từ precss của parameters //Tiếp theo cho đối tượng bay nhảy gì gì đó tùy ý, dùng .animate() của jQuery, dùng css và time của parameters //Cuối cùng là gọi hàm callback sau khi animate() xong, dùng callback của parameters }); }
Code:
init: function(parameters) { var defaults={precss: {}, css: {}, time: 2000, callback: function(){}}; parameters=$.extend({}, defaults, parameters); this.each(function() { //Đặt tên 1 biến để dễ tham chiếu nào var obj=$(this);//Nhớ rằng mình dùng hàm của jQuery, nên phải biến nó thành object //Đầu tiên đặt css cho đối tượng, lấy thông tin từ precss của parameters obj.css(parameters["precss"]); //Tiếp theo cho đối tượng bay nhảy gì gì đó tùy ý, dùng .animate() của jQuery, dùng css và time của parameters obj.animate(parameters["css"], parameters["time"]); //Cuối cùng là gọi hàm callback sau khi animate() xong, dùng callback của parameters }); }
Code:
function saySomething(a) { alert(a); } saySomething.call(this, "HELLO");
Code:
init: function(parameters) { var defaults={precss: {}, css: {}, time: 2000, callback: function(){}}; parameters=$.extend({}, defaults, parameters); this.each(function() { //Đặt tên 1 biến để dễ tham chiếu nào var obj=$(this);//Nhớ rằng mình dùng hàm của jQuery, nên phải biến nó thành object //Đầu tiên đặt css cho đối tượng, lấy thông tin từ precss của parameters obj.css(parameters["precss"]); //Tiếp theo cho đối tượng bay nhảy gì gì đó tùy ý, dùng .animate() của jQuery, dùng css và time của parameters obj.animate(parameters["css"], parameters["time"], function(){ //Cuối cùng là gọi hàm callback sau khi animate() xong, dùng callback của parameters if(typeof parameters["callback"]=="function")//Kiểm tra coi có phải là hàm ko { parameters["callback"].call(this, null);//Không có tham số nào được truyền vào. } }); }); }
Code:
$("#test").fly( { precss: {position: "absolute", background: "#000000"}, css: {width: 600, height: 600, top: 0, right: 0}, time: 2000, callback: function(settings) { var w=settings["css"]["width"]; var h=settings["css"]["height"]; var t=settings["time"]; this.fly( { css: {width: w-400, height: h-400, top: 0, left: 0}, time: t*2, callback: function() { alert("Da hoan tat!"); } }); } });
Code:
parameters["callback"].call(this, null);
Code:
parameters["callback"].call(this, parameters);
Thân.
[x] - xx3004
Bonus: Ví dụ trên, nhưng được viết dưới dạng phức tạp hơn rất nhiều, ứng dụng nhuần nhuyễn hơn những điều đã nói ở trên, mức độ KHÓ, bạn không nên đọc tiếp nếu bạn chưa nhuyễn những điều ở trên:
Code:
(function($) { xm= { init: function(o, d)//Options, Defaults to merge { o=(o!="" && o!=null) ? o : {}; d=(d!=""&&d!=null) ? d : {}; return $.extend({},d,o); }, exec: function($_)//[Function and parameters callback] { var defaults={func: function(){}, parameters: null, callback: null}; $_=this._($_, defaults); f=$_["func"]; p=($_["parameters"]==undefined) ? null : $_["parameters"]; cb=$_["callback"]; if(typeof f=="function"){f.call(this, p);} else{this._("error", {error: "Not a [function]"});} //Call back if(cb){this._("exec", {func: cb});} }, error: function($_)//[Error] { var defaults={error: null}; $_=this._($_, defaults); e=$_["error"]; if(e){this._.errors=(this._.errors!=null) ? this._.errors+" [AND] "+e : e;}else{return this._.errors;} }, data: function($_)//[Name Data] { var defaults={name: null, data: null}; $_=this._($_, defaults); var d=$_["name"], v=$_["data"]; this._.data=(this._.data==null) ? {} : this._.data; if(d && v){this._.data[d]=v;}else{return this._.data[d];} } }; $.fn.x=$.fn._=function(m){if(xm[m]){this._.errors=(!this._.errors)?null:this._.errors;return xm[m].apply(this,Array.prototype.slice.call(arguments,1))}else if(typeof m=="object"||!m){this.errors=(!this.errors)?null:this.errors;return xm.init.apply(this,arguments)}else{this.errors=(!this.errors)?null:this.errors;this._("error", {error: "Method "+m+" does not exist"})}} })(jQuery); (function($) { methods= { init: function(parameters) { var defaults={precss: {}, css: {}, time: 2000, callback: function(){}}; parameters=this._(parameters, defaults); this.each(function() { var obj=$(this); obj.css(parameters["precss"]); obj.animate(parameters["css"], parameters["time"], function() { this._("exec", {func: parameters["callback"], parameters: parameters}); }); }); } } $.fn.fly=function(m) { if(methods[m]) {return methods[m].apply(this,Array.prototype.slice.call(arguments,1));} else if(typeof m=="object"||!m) {return methods.init.apply(this,arguments);} else { this._("error", {error: "Method "+m+" does not exist!"}); alert(this._("error")); } } })(jQuery);
Bài liên quan
P/S: Để có gì lát về chia nhỏ nó lại!
[x]
Để vậy là dễ đọc rồi đó chớ
[x]
[x]