Các bước cơ bản để tạo plugin bằng jQuery

Trong bài viết dưới đây, chúng tôi sẽ giới thiệu và hướng dẫn các bạn một vài bước cơ bản để tạo plugin bằng jQuery. Cho dù bạn là người mới bắt đầu tìm hiểu về lĩnh vực phát triển web hoặc đã có thời gian dài tiếp xúc với JavaScript thì quả thực jQuery là 1 nền tảng framework tuyệt vời và không thể bỏ qua, đặc biệt là những người đến với jQuery từ nền tảng Javascript có sẵn.

Chúng ta gần như không thể hiểu được jQuery nếu không nắm rõ CSS Selectors:

Trong jQuery thì phần mã CSS selector được bắt đầu bởi đối tượng jQuery $ chính, đây chỉ là tên của function, và nó có đi kèm với tham số – chính là CSS Selector của chúng ta. Và về bản chất thì Selector hoạt động tương tự như các CSS selector thường gặp trong các file style.css. Nhiều người chỉ biết đến ID selector (#)class selector (.) nhưng chúng chỉ là những đối tượng nhất định thuộc về bộ Selectors:

CSS Selectors

Nếu bạn có thể hiểu được các chi tiết trong biểu đồ trên thì những phần còn lại của jQuery cũng không có gì khó khăn để nắm bắt. Và một khi đã biết được những nguyên tắc cơ bản nhất của quy trình viết plugin jQuery thì hầu như không có ai muốn quay trở về với Javascript. Dưới đây là một số câu hỏi thường gặp khi chúng ta bắt tay vào quá trình viết plugin jQuery:

  • CSS Selectors là gì?
  • Sự khác biệt khi sử dụng $.myfunction$.fn.myfunction là gì?
  • Ký hiệu $ nghĩa là gì?
  • Chức năng chính và cách sử dụng của function jQuery.extend?
  • Làm thế nào để khởi tạo plugin jQuery và chuyển tới tham số chức năng?
  • Làm thế nào để truyền giá trị default và khởi tạo tham số override?

Đối tượng trong JavaScript:

Như tất cả chúng ta đã biết rằng, JavaScript là 1 ngôn ngữ lập trình hướng đối tượng, và nó không phù hợp để mô tả hoặc trình diễn những tính năng của jQuery với những người nào đó chưa từng nghiên cứu về khái niệm đối tượng trong JavaScript. Đặc điểm cơ bản về thiết kế đối tượng trong JavaScript là khi người sử dụng khai báo hoặc khởi tạo 1 function, thì function đó sẽ là 1 class ở chế độ mặc định, và sau đó sẽ được dùng như 1 đối tượng.

Bên cạnh đó, chúng ta có thể sử dụng toán tử new để tạo bản copy của cùng class. Và nó chỉ xảy ra khi người dùng thay thế bằng từ khóa class quen thuộc, JavaScript sẽ ưu tiên từ khóa function hơn. Lưu ý rằng class trong JavaScript có thể được sử dụng dưới dạng 1 class đã được kế thừa – inhertited từ class khác (bằng cách thực hiện các prototype inheritance) hoặc được xây dựng chính xác như 1 function từ trong mã nguồn. Nói theo cách ngắn gọn thì trong JavaScript, tất cả đều là đối tượng.

Mặt khác, function closures thường được sử dụng với các event trong jQuery. 1 function closure điển hình chính là 1 loại function không có tùy biến hoặc tên bất kỳ, mà đơn giản chỉ là 1 đối tượng của function đó.

Các đối tượng chính trong jQuery:

Về mặt bản chất, thì các đối tượng chính của jQuery đều được định nghĩa với từ khóa function như 1 đối tượng với tên định danh là $, và chúng còn tương đương với global function, chẳng hạn như window.$. Ví dụ, trong JavaScript khi chúng ta mở rộng một đối tượng window nào đó (phải có sẵn trong trình duyệt từ khâu thiết kế), hoặc nói theo cách khác là gán thêm function member với (.) tới đối tượng window, và cũng có nghĩa rằng chúng ta có thể gọi đối tượng đó bằng window.myobj(); hoặc đơn giản là myobj();. Bởi vì với mỗi function được gắn với object window đều có thể được gọi từ global scope trong toàn bộ script. Còn từ bên trong, các đối tượng của jQuery sẽ được tạo như sau:


var jQuery = window.jQuery = window.$ = function(selector, context)
{
// …
// other internal initialization code goes here
};

Thuật toán khai báo chính xác này cho phép người dùng dễ dàng chuyển đến các đối tượng jQuery chính qua tên jQuery hoặc ký hiệu $. Các bạn cần biết rằng jQuery, window.jQuery, window.$ hoặc đơn giản chỉ là $ đều có thể được sử dụng thay thế cho nhau, bởi vì quá trình khai báo ở phần đầu của bộ mã, và nó có liên quan đến toàn bộ đối tượng.

1 điểm nữa cần lưu ý là tham số selector và context của jQuery có chức năng hoạt động như 1 object bình thường. Selector thường là chuỗi – string có chức năng lựa chọn 1 số lượng nhất định các element từ DOM Document Object Model. Hoặc cũng có thể là đối tượng this (self reference). Tham số jQuery selector có thể chấp nhận nhiều giá trị giống nhau trong trường hợp người dùng muốn sử dụng trong quá trình khởi tạo style của CSS. Ví dụ, các bạn hãy tham khảo quá trình gọi đối tượng trong jQuery như sau:

	// Select all elements of class: "someClass" and
// apply a red border to all of them
jQuery(".someClass").css("border", "1px solid red");

// Select an element with id: someId and insert dynamic html into it jQuery("#someId").html("<b>So Bold!</b>");

// Exactly the same as above, but using $ $("#someId").html("<b>So Bold!</b>");

Rất ngắn gọn và đơn giản, các bạn không còn phải tự viết function document.getElementById vốn rất dễ gây nhầm lẫn. Chỉ với 1 dòng lệnh, jQuery sẽ chọn tất cả các thành phần theo yêu cầu bằng các rà soát toàn bộ DOM và áp dụng “hiệu lực”. Một điểm rất đáng chú ý nữa của jQuery là chế độ tương thích với tất cả các trình duyệt hiện nay.

Điểm mấu chốt của plugin jQuery:

Tại đây, chúng ta sẽ bắt đầu với ví dụ đơn giản về plugin jQuery khá phổ biến. Cụ thể, khi mới bắt đầu tìm hiểu về 1 ngôn ngữ lập trình hoặc nền tảng framework nào đó thì chúng ta đều phải biết được điểm mấu chốt của vấn đề. Trong thời điểm jQuery chưa xuất hiện, những lập trình viên JavaScript thường có thói quen thực thi các đoạn mã, chức năng quan trọng trong function window.onload như minh họa dưới đây:

		// Override the onload event
window.onload = function()
{
    // the page finished loading, do something here...
}

Thực chất, đoạn mã trên chỉ thực hiện nhiệm vụ ghi đè lên event onload của thẻ <body> trong HTML, hoặc hiểu nôm na rằng đoạn mã của chúng ta chỉ được thực thi khi trang web hoàn tất quá trình tải. Trong nhiều trường hợp thì việc này lại rất khó khăn vì có những trang cần nhiều thời gian để tải hết nội dung, hoặc quá trình tải bị gián đoạn do cấu trúc duyệt dữ liệu khác nhau.

Tại đây, chúng ta không muốn compile và thực thi bất kỳ đoạn mã JavaScript nào trên trang đang được tải. Kiến trúc bên trong của jQuery tận dụng tối đa ưu điểm của event window.onload, nhưng trước đó cũng thực hiện việc kiểm tra toàn bộ DOM đã được tải hay chưa, vì quy trình này rất quan trọng.

Tuy nhiên, những yếu tố trên lại chưa đủ để jQuery “biết” trang web đã được tải hoàn tất hay chưa, mà chúng ta phải chắc chắn rằng DOM đã được “xây dựng” đầy đủ – thực hiện bằng cách “lắng nghe” tín hiệu DOMContentLoaded trên hầu hết tất cả các trình duyệt. Nhưng thật là may mắn vì chúng ta không cần phải thực hiện công đoạn kiểm tra như trên, jQuery đã đảm nhận toàn bộ việc này.

Để hỗ trợ người sử dụng, jQuery đã cung cấp 1 method mới với tên gọi ready mà chúng ta có thể gọi ra từ chính đối tượng chính của jQuery. Cụ thể, khi viết plugin jQuery, các bạn sử dụng function ready này để kiểm tra xem hệ thống đã sẵn sàng để thực thi đoạn mã đó hay chưa. Lưu ý rằng đây không phải mã plugin chính mà là điểm mấu chốt của plugin. Hoặc hiểu nôm na rằng đây là phiên bản jQuery của function window.onload:

		<script type = "text/javascript">
 // Define the entry point
 $(document).ready(function()
 {
 // The DOM (document object model) is constructed
 // We will initialize and run our plugin here
 });
 </script>

Ví dụ tại đây, chúng ta sẽ viết 1 plugin với tên gọi là Shuffle, và plugin này có 2 chức năng riêng rẽ để khởi tạo cũng như thực thi phần mã cơ bản, có dạng như sau:

		<script type = "text/javascript">
 // One way to initialize plugin code
 $(document).ready(function()
 {
 jQuery.Shuffle.initialize( "monalisa.jpg", 5, 8, 67, 1500);
 jQuery.Shuffle.run();
 });
 </script>

Như thường lệ thì bất kỳ đoạn mã nào cũng có thể được tùy biến dựa vào nhu cầu của người sử dụng. Cú pháp sử dụng dưới đây được coi là phổ biến, bởi nó được áp dụng bởi phần lớn lập trình viên jQuery, tuy nhiên lại rất dễ gây nhầm lẫn đặc biệt là với những người mới bắt đầu tìm hiểu về cả JavaScript jQuery:

		<script type = "text/javascript">
 (function($){ ... })(jQuery);
 </script>

Đây là trường hợp rất hay gặp trong trực tế, dấu 3 chấm thường được thay thế bằng đoạn mã cần thực thi. Cụ thể tại đây, các bạn cần tham khảo về định nghĩa của function anonymous (hay còn gọi là function closure) với tham số đi kèm là $. Và phần chức năng này được bao trong dấu ngoặc đơn, vì sao vậy? Function trong ví dụ này thực chất là 1 function anonymous không tham chiếu đến chính bản thân nó, nhưng vì ở trong “phạm vi” của dấu ngoặc đơn nên JavaScript sẽ cho phép người dùng tham chiếu đến bất kỳ function anonymous đã được tạo.

Bên cạnh đó, chúng ta có thể thêm 1 dấu chấm sau khi phần khởi tạo của function được đặt trong dấu ngoặc đơn, và gọi ra 1 function member được hỗ trợ bởi function này. Tiếp theo, thêm dấu chấm ở phía cuối mệnh đề đó và gọi tiếp 1 function member khác có thể được thực thi dựa trên định dạng của đối tượng được trả về bởi function trước. Tính năng function chaining này của JavaScript khá phổ biến và được áp dụng trong nhiều ngôn ngữ lập trình khác, chẳng hạn như Perl. Đó là 1 dạng tính năng của ngồn ngữ kịch bản vì nó giúp cho phần mã của người dùng trở nên ngắn gọn, dễ quản lý và trực quan hơn.

Do vậy, bằng cách đặt function anonymous trong dấu ngoặc đơn thì chúng ta có thể dễ dàng tham chiếu đến phần bộ nhớ của chính function đó mà không cần phải qua tên – mà thực ra cũng không thể làm được vì function không có tên.

Mặt khác, các bạn còn có thể gọi function đó bằng mệnh đề được dùng để tạo function. Và đó cũng chính là cách làm của chúng ta tại đây. Các function không có tên được định nghĩa, đặt trong dấu ngoặc đơn và sẽ được gọi ngay sau đó. Đây chỉ là 1 ví dụ đơn giản về cách sử dụng của function closure mà chúng ta có thể dễ dàng nhận ra trong nhiều phần mã nguồn jQuery và JavaScript nâng cao.

Những lý do để làm như vậy là gì? Thứ nhất là để rút gọn tối đa độ dài của code, tiếp theo là ẩn toàn bộ các tham số được khởi đầu bằng $ khỏi mô hình global scope tổng thể. Bên cạnh đó, đối với những trường hợp chúng ta dùng nhiều nền tảng framework khác nhau thì việc sử dụng ký hiệu $ đối với object function trong mô hình của toàn bộ chương trình rất dễ gây nhầm lẫn.

Nhưng trong thực tế thì điều đó lại hoàn toàn phụ thuộc vào hoàn cảnh và sự lựa chọn chính của người phát triển. Cụ thể, trong phần tiếp theo của bài hướng dẫn thì chúng tôi không hề sử dụng bất cứ framework hoặc mã bổ sung bên ngoài, cú pháp chung có thể hơi loằng ngoằng và khó hiểu, nhưng tỉ lệ xung đột và lỗi phát sinh sẽ được đảm bảo ở mức thấp nhất. Cú pháp cơ bản chung như sau:

		<script type = "text/javascript">
 jQuery.func = doSomething;
 jQuery.fn.func = doSomethingElse;
 </script>

Với doSomething doSomethingElse đơn giản chỉ là những đối tượng function đã được định nghĩa và khởi tạo trước đó, nhưng nếu làm như vậy thì chúng ta đồng thời cũng mất đi khả năng kết hợp, xâu chuỗi plugin jQuery với các function API đã có sẵn. Cuối cùng, nếu các bạn muốn plugin có khả năng chaining thì phải sử dụng function sell-calling như đã giải thích ở trên. Nếu kết hợp chức năng chaining của plugin với mã của nhiều dự án khác, các bạn chỉ cần ghép đối tượng và biến cần thiết tới đối tượng $. hoặc $.fn. Lưu ý rằng trong jQuery, một số ký tự như dấu $ hoặc một vài trường hợp đặc biệt như window.$ có thể được sử dụng thay thế nhau, đồng thời còn được dùng để đề cập chính xác tới 1 đối tượng bất kỳ nào đó đã lưu trong bộ nhớ. Trong tài liệu hướng dẫn cụ thể của jQuery cũng đã chỉ ra rằng chúng ta nên sử dụng đối tượng jQuery thay cho dấu $. Vì ký hiệu $ phải được ẩn, cho nên chỉ sử dụng trong toàn bộ cấu trúc riêng của jQuery mà thôi, và tuyệt đối không được “tiếp xúc” với bất kỳ tham số $ nào tới phần implementators của plugin.

jQuery Plugin Design Pattern `A`

Tiếp theo, chúng ta sẽ tham khảo về phần mã internal của plugin tùy ý với function anonymous self-referencing và chức năng lấy các đối tượng jQuery dưới dạng tham số. Nhưng tại đây, chúng ta sẽ dễ bị nhầm lẫn với từ khóa this được tham chiếu tới trong phần pattern này và các vị trí khác trong toàn bộ code. Cụ thể, chúng tôi đã tạo một số biến local với tên là vari – được lưu trữ trong đối tượng jQuery chính ($.vari) và đối tượng jQuery fn ($.fn.vari). Chúng cũng có thể là object function, nhưng để đơn giản và dễ hiểu thì chúng ta chỉ sử dụng phần biến cơ bản mà thôi. Các bạn sẽ dễ dàng thấy những giá trị này hiển thị cùng với chức năng cảnh báo – alert trong đoạn mã dưới đây.

Trước tiên là phần pattern chưa rút gọn:

			(function($)
{
    $.vari = "$.vari";
    $.fn.vari = "$.fn.vari";

// $.fn is the object we add our custom functions to $.fn.DoSomethingLocal = function() { return this.each(function() { alert(this.vari); // would output `undefined` alert($(this).vari); // would output `$.fn.vari` }); }; })(jQuery);

// $ is the main jQuery object, we can attach a global function to it $.DoSomethingGlobal = function() { alert("Do Something Globally, where `this.vari` = " + this.vari); };

$(document).ready(function() { $("div").DoSomethingLocal(); $.DoSomethingGlobal(); });

Và phần chi tiết cụ thể: 

			// plugin-name.js - define your plugin implementation pattern
(function($) // The $ here signifies a parameter name
             // As you can see from below, (jQuery) is
             // immediately passed as the $ param
{
    $.vari = "$.vari";
    $.fn.vari = "$.fn.vari";

// 1.) Add a custom interface `DoSomethingLocal` // Which will modify all selected elements! // If you are a software engineer, think about this as // a member function of the main jQuery class $.fn.DoSomethingLocal = function() { // return the object back to the chained call flow return this.each(function() // This is the main processor // function that executes on // each selected element // (e.g: jQuery("div")) { // this ~ refers to a DOM element // $(this) ~ refers to a jQuery object

// Here, the `this` keyword is a self-refence to the // selected object `this.vari` is `undefined` because // it refers to selected DOM elements. So, we can do // something like: var borderStyle = this.style.border; // While $(this).vari, or jQuery(this).vari refers // to `$.fn.vari`

// You would use the $(this) object to perform // any desired modification to the selected elements // $(this) is simply a reference to the jQuery object // of the selected elements alert(this.vari); // would output `undefined` alert($(this).vari); // would output `$.fn.vari` }); }; })(jQuery); // pass the jQuery object to this function

// 2.) Or we can add a custom interface to the global jQuery // object. In this case, it makes no sense to enumerate // through objects with `each` keyword because this function // will theoretically work in the `global` scope. If you are // a professional software engineer, think about this // as a [static function] $.DoSomethingGlobal = function() { // this will output this.vari = $.vari alert("Do Something Globally, where `this.vari` = " + this.vari); };

// index.html - test the plugin $(document).ready(function() { $("div").DoSomethingLocal(); $.DoSomethingGlobal(); });

Thực ra, có 2 dạng giao diện khác nhau mà chúng ta có thể gán vào object jQuery chính đã được khởi tạo sẵn bởi framework. Các bạn hãy tham khảo kỹ ví dụ trên, tại những nơi chúng tôi đã gán thêm 2 function, đó là DoSomethingLocal DoSomethingGlobal.

  • DoSomethingLocal được định nghĩa thành $.fn.DoSomethingLocal. Người dùng có thể gán nhiền function tùy chỉnh tới đối tượng jQuery.fn (hoặc $.fn) được yêu cầu bởi quá trình thực hiện plugin. Bất kỳ function nào được gán tới đối tượng $.fn đều hoạt động trên phần tùy chỉnh của 1 đối tượng hoặc thành phần nào đó được lựa chọn. Và đây cũng chính là ý nghĩa của cú pháp $.fn.
  • DoSomethingGlobal được áp dụng trực tiếp vào đối tượng global jQuery dưới dạng $.DoSomethingGlobal. 1 function được gán tới framework jQuery theo cách như vậy sẽ không hoạt động trên thành phần được lựa chọn, nhưng lại có thể hoạt động trong global scope và có chứa bất kỳ khả năng thực hiện nào.

jQuery Plugin Design Pattern `B`

Trên thực tế thì nhiều người lại thích đặt đoạn mã của plugin trong function anonymous, và gọi ra bằng cách gắn thêm dấu ngoặc đơn ở phía cuối mệnh đề, sau đó chuyển đối tượng jQuery đó tới giống như phần jQuery Plugin Design Pattern `A` trên, nhưng liệu chúng ta có thực sự cần phải làm như vậy? Không. Hãy tham khảo 1 cách khác để tạo plugin jQuery với cấu trúc đơn giản và dễ dàng hơn.

Phần pattern dưới đây phải được sử dụng trong trường hợp chúng ta không thực hiện chức năng chainability, hoặc nói theo cách khác là function của plugin sẽ không trả lại đối tượng jQuery.

			// Plugin base object
$.gShuffle = function()
{

}

// Initializes plugin $.gShuffle.initialize = function() {

}

// Runs the plugin $.gShuffle.run = function() {

};

Hy vọng rằng những thông tin trên sẽ giúp bạn hiểu hơn về các bước cơ bản tạo plugin jQuery. Chúc các bạn thành công!

Theo Quản Trị Mạng.