کمی بیشتر همراه با DOM
jQuery باز هم بهترین گزینه خواهد بود اگر تصور شما بر آن است که تمام کاری که جاوااسکریپت انجام می دهد به "بده بستان" با صفحه ختم می شود. این موضوع زمانی نمود پیدا می کند که این سیستم قدرتمند معمولا ظاهری شبیه به برنامه نویسی های معموال را ندارد. فعالیتهای دیگر در جاوااسکریپت هم توسط این سیستم قابل اجراست ولی اگر تمرکز و فوکوس شما روی DOM ،CSS، متحرک سازی، دریافت اطلاعات توسط AJAX است، jQuery این مسئله را پوشش می دهد در حالیکه نیاز به سبک معمولی و دمده جاوااسکریپت هم نباشد. jQuery متدهایی را برای موارد غیر از DOM نیز ارائه می دهد؛ مانند روشی که برای شمارش آرایه ها دارد - (each(array, fn.$ – یا متدی برای پالایش رشته ها - (trim(str.$ . اما وجود اکثر این متدها چندان حیاتی نیستند وقتی فعالیت روی DOM و دسترسی به اجزای آن انجام می شود (از قبیل کار با CSS، مدیریت اطلاعات HTML و تعریف رویداد هنگام کلیک شدن بخشی از صفحه و غیره).
اما اگر عمیقتر به آن نگاه کنید متوجه خواهید شد که jQuery تمرکزی روی فعالیتهای خارج از DOM ندارد. این یکی از دلایلی است که یادگیری ساده آن را به ارمغان آورده ولی محدودیت در نگارش جاوااسکریپت هم با آن همراه شده. این توجیهی است که او تمایلی به حرکت از یک سیستم خشک مختص DOM را ندارد. او از خواص ارث بری و حتی امکانات ابتدایی محلی برای متغیرهای جاوااسکریپت محروم است، در واقع او نیازی هم به آنها ندارد. اگر نیاز به استفاده از regular expressions، arrays، strings، dates، functions را دارید، جاوااسکریپت کمک شما خواهد بود. jQuery برای شما DOM را به یک زمین بازی تبدیل می کند ولی سایر فعالیتها در حیطه تعریف آن نیستند.
این نکته ای است که MooTools را کاملا متمایز می سازد. به غیر از تمرکز انحصاری روی DOM، گستره این فریم ورک به کل زبان جاوااسکریپت می رسد (باید اضافه کرد که براساس تجربه ای که من با jQuery داشته ام، تمام فعالیتهای ممکن با jQuery، با MooTools هم قابل اجراست ولی به شکلی کاملا متفاوت). اگر jQuery محیط DOM را به زمین بازی شما مبدل می کند MooTools خود زبان جاوااسکریپت را زمین بازی شما می کند و این یکی از دلایلی است که یادگیری اش را مشکلتر ساخته.
ارث بری یا Inheritance در جاوااسکریپت
زبان جاوااسکریپت دارای قابلیتهای فوق العاده ای است. برای تازه کارها، این زبان براساس توابع یا functional است به این معنا که توابع به عنوان اشیاء رده بالایی اجرا میشوند که امکان ارسالشان بصورت متغیر وجود دارد مانند اشیاء String و Number و غیره. این زبان با همین مفهوم ذهنی طراحی شده و زمانی که از این روش استفاده می کنید اکثر متدها و الگوها(Patterns) بخوبی کار می کنند. به اختلاف بین دو مورد زیر توجه کنید:
for (var i = 0; i < myArray.length; i++) { /* do stuff */ }
for (var i = 0; i < myArray.length; i++) { /* do stuff */ }
و
myArray.forEach(function(item, index) { /* do stuff */ });
myArray.forEach(function(item, index) { /* do stuff */ });
جاوااسکریپت دارای یک مدل ارث بری است. این مدل با اینکه منحصر به فرد نیست ولی کم نظیر است. در این زبان بجای تعریف کلاس و نمونه گیری آن، ارث بری بصورت prototypal صورت می گیرد. بدین معنا که اشیاء مستقیما از اشیاء دیگر بدست می آیند (به ارث می رسند – inherit می شوند). اگر در یک شیء، مشخصه یا Propertyای درخواست گردد، جاوااسکریپت به دنبال آن در شیء فرزند (Child) جستجو می کند و اگر پیدا نکرد جستجو را در شی والد (Parent) ادامه می دهد. به همین دلیل است که یک آرایه، به هنگام تعریف می تواند متدهای آرایه ها را بپذیرد:
[1,2,3].forEach(function(item) { alert(item) }); //this alerts 1 then 2 then 3
[1,2,3].forEach(function(item) { alert(item) })
متدی با عنوان "forEach" برای آرایه بالا تعریف نشده پس جاوااسکریپت به دنبال این متد در شیء والد یعنی شیء Arrays می گردد. وقتی این متد را برای شیء از نوع آرایه بکار می برید، این زبان، در شیء شما، این متد را درخواست می کند و اگر متدی با این نام وجود نداشت، به سراغ متدهای شیء مخصوص همه آرایه ها می رود. این بدین معناست که متد forEachدر حافظه مربوط به شیء بالا قرار نگرفته بلکه در حافظه مربوط به شیء اصلی آرایه ها (prototype of arrays) جای دارد. این یک شیوه فوق العاده کارآمد و مفید است (شایان گفتن است که متد each در MooTools متناظر با متد forEach است).
مرجع درونی یا Self refrence
جاوااسکریپت یک عبارت خاص دارد: “this”. تعریف اجمالی این عبارت برای من کمی مشکل است ولی در کل می توان گفت این عبارت در یک متد، به شیءای که متد در آن واقع است اشاره دارد. این قابلیت به اشیاء امکان ارجاع به خودشان را در متدهایشان می دهد و کاربرد دیگری ندارد. اهمیت این ویژگی اینجاست که وقتی شما اشیاء فرزند و نمونه های متعددی از آنها را داشته باشید چطور متدها را، به شیء خودشان ارجاع می دهید؟ وقتی متد در کلاس والد وجود داشته باشد و در کلاس فرزند نباشد، این عبارت امکان ارجاع به متد اصلی را به نمونه ها می دهد (توضیحات تفصیلی درباره عبارت this و مقاله ای دیگر در موزیلا).
عبارت this به اشیایی که به ارث برده شده اند، امکان ارجاع به متدهای خودشان را می دهد. اما گاهی پیش می آید که شما نیاز به ارجاع به مکان دیگری از طریق this را دارید که در این حالت باید از binding استفاده شود. binding مقدار متفاوتی را برای this در متد مشخص می کند. متد each از طریق پارامتر دوم خود امکان تعیین منبع شیء را فراهم می کند. به مثال زیر توجه کنید:
var ninja = { weapons: ['katana', 'throwing stars', 'exploding palm technique'], log: function(message) { console.log(message); }, logInventory: function() { this.weapons.each(function(weapon) { //we want "this" to point to ninja... this.log('this ninja can kill with its ' + weapon); }, this); //so we pass "this" (which is ninja) to Array.each } }; ninja.logInventory(); //this ninja can kill with its katana //this ninja can kill with its throwing stars //this ninja can kill with its exploding palm technique
var ninja = {
weapons: ['katana', 'throwing stars', 'exploding palm technique'],
log: function(message) {
console.log(message);
},
logInventory: function() {
this.weapons.each(function(weapon) {
//we want "this" to point to ninja...
this.log('this ninja can kill with its ' + weapon);
}, this); //so we pass "this" (which is ninja) to Array.each
}
};
ninja.logInventory();
//this ninja can kill with its katana
//this ninja can kill with its throwing stars
//this ninja can kill with its exploding palm technique
در مثال بالا و در متد logInventory ، ما ninja را به متدی که به آرایه منصوب شده ارجاع دادیم که می تواند عملیات log را بر روی خود ninja انجام دهد در حالیکه اگر با استفاده از پارامتر دوم each، شیء را به اصطلاح bind نمی کردیم، عبارت this همچنان به window اشاره می داشت.
اینها نمونه هایی از روشهای قدرتمند جاوااسکریپت برای ارث بری، ارجاع درونی و binding و مشخصه های سودمند prototype بودند. نکته نامطلوب این است که جاوااسکریپت این امکانات را بطور کامل و سودمند ارائه نمی دهد و اینجاست که MooTools جلوه نمایی خواهد کرد. MooTools این الگوها را بسیار ساده و دلپذیر در دسترس قرار می دهد. شما می توانید کدهای خود را با کدهای abstract بیشتری تولید کنید (مترجم: الگوی abstract امکاناتی برای معرفی منابع ارجاعات به کدها را فراهم می کند) که در یک اجرای طولانی، بسیار قابل استفاده و قدرتمند نمایان می شود. فهمیدن اینکه الگوها چطور می توانند مقدارپذیر (valuable) باشند و چطور باید از آنها استفاده کرد به کمی تلاش نیازمند است اما در نهایت کدهای شما بطور فوق العاده ای چندبار مصرف (reusable) و قابل مدیریت می شود. درباره این دو خصوصیت در ادامه بحث خواهد شد.
MooTools جاوااسکریپت را لذتبخش تر می کند
از آانجاییکه تمــرکز MooTools بر روی APIهای جاوااســکریــپت است، این زبان را پایدارتر و منسجم تر می کند. این فریم ورک تلاش زیادی برای تغییر شیوه کدنویسی نمی کند بلکه فوکوسی ویژه برای دلپذیرتر و قابل تحملتر کردن جاوااسکریپت دارد. می توان گفت در واقع MooTools یک extension یا توسعه برای زبان جاوااسکریپت است. سعی او بر آن است که جاوااسکریپت را به موقعیتی که باید باشد برساند. بخش اعظم هسته آن به تقویت اشیاء Function، String، Array، Number، Element و غیره اختصاص دارد و بخش دیگر امکانی مهمی که ارائه می دهد تابعیست با نام Class.
حالا Classشباهت بیشتری به روشهای تعریف الگوهای ارث بری که در زبانهایی مثل Java یا ++C سراغ داریم، دارد اما این همه موضـــوع نــیست. کاری که Class انجام می دهد دسترسی و استفاده آسان از مدل ارث بری prototypal است (مترجم: شیء گرایی در بسیاری از زبانها بر پایه تعریف کلاس است و در جاوااسکریپت بر پایه prototype به معنای دسترسی و ارتباط نمونه هاست). این خواص توسط MooTools معرفی نشده اند و منحصر به فرد نیستند اما jQuery فاقد هر دوی آنهاست. jQuery هیچ مدلی برای ارث بری ندارد و امکاناتی هم برای اشیاء محلی مثل String و Array تعریف نکرده. این یک کاستی ساده نیست که نویسندگان jQuery به سادگی جبران کنند در واقع می توان گفت آنها ابزاری طراحی کرده اند با مقاصد متفاوت. همانطور که MooTools جاوااسکریپت را مفرح می سازد، jQuery نیز کار با DOM را دلپذیرتر می کند و آنها با این دیدگاه، محدودیتهای خود را پذیرفته اند.
jQuery استفاده از DOM را لذتبخش تر می کند
و این دلیل قابل دسترس تر بودن jQuery است. او از شما فراگیری جاوااسکریپت را نمی خواهد. او شما را به اعماق مدلهای ارث بری و کاربرد this و binding و اشیاء محلی پرتاب نمی کند. وقتی از طریق آموزش رسمی خود jQuery، به یادگیری آن اقدام می کنید، این اولین مثالی است که آورده شده:
window.onload = function() { alert("welcome"); }
window.onload = function() {
alert("welcome");
}
و این سومین است:
$(document).ready(function() { $("a").click(function(event) { alert("Thanks for visiting!"); }); });
$(document).ready(function() {
$("a").click(function(event) {
alert("Thanks for visiting!");
});
});
اگر فراگیری MooTools را از طریق کتاب آن یا آموزش اینترنتی آن (که مؤلف هر دوی آنها من هستم) آغاز کنید، خواهید دید که شیوه معرفی کاملا متفاوت است. با اینکه در آموزش MooTools امکان نادیده گرفتن ترتیب مباحث وجود دارد و می توان مستقیما سراغ افکتها و بخشهای DOM رفت ولی این آموزش با طرح چیزهایی مانند Class یا پرسیدن سوالاتی از قبیل آیا شما یک برنامه نویس مبتدی هستید یا آیا فقط قصد اضافه کردن امکاناتی به سایت خود هستید، شروع می شود. با این وضعیت، jQuery برای شما بسیار دوستانه تر است.
به عبارت دیگر اگر قصد یادگیری خود زبان جاوااسکریپت را دارید، MooTools بهترین وسیله است. او امکاناتی را معرفی می کند که زبان جاوااسکریپت در حال حرکت به سمت آنهاست (بسیاری از متدهای Native در جاوااسکریپت 1.8 و بالاتر معرفی خواهند شد). اگر به برنامه نویسی آشنا هستید مخصوصا به مدلهای شیء گرایی و تابعی (functional)، فریم ورک MooTools الگوهای طراحی شگفت انگیز و فوق العاده زیادی را داراست.
هر کاری که تو بتونی، من بهترش رو می تونم
اغلب به ازای امکاناتی که jQuery ارائه داده است نمونه مشابهی در MooTools یافت می شود. اما این ویژگی برای jQuery صادق نیست و بسیاری از امکانات MooTools را نمی توان در jQuery شبیه سازی کرد زیرا همانطور که گفته شد، تمرکز آن بر روی DOM قرار دارد. دامنه امکانات MooTools از jQuery بیشتر است ولی شما از استفاده گسترده تر منع نشده اید. برای مثال jQuery فاقد سیستم ارث بری است اما این مشکلی نیست زیرا می توانید از قابلیت Class موجود در MooTools بصورت تلفیقی در jQuery بهره ببرید (یا چنین قابلیتی را خودتان در jQuery طراحی کنید) و یا حتی از پلاگین مخصوص ارث بری در آن استفاده کنید (من از این پلاگین استفاده نکرده ام ولی اینطور به نظر می رسد که قابلیتهای خوبی برای این منظور داشته باشد).
اگر به مثال ذیل از jQuery دقت کنید:
$(document).ready(function() { $("a").click(function(event) { alert("Thanks for visiting!"); }); });
$(document).ready(function() {
$("a").click(function(event) {
alert("Thanks for visiting!");
});
});
و آن را به MooTools ترجمه کنید، خواهید داشت:
window.addEvent('domready', function() { $$('a').addEvent('click', function(event) { alert('Thanks for visiting!'); }); });
window.addEvent('domready', function() {
$$('a').addEvent('click', function(event) {
alert('Thanks for visiting!');
});
});
شباهت زیادی دارند، اینطور نیست؟
این یک مثال پیچیده تر از jQuery است:
$(document).ready(function() { $("#orderedlist li:last").hover(function() { $(this).addClass("green"); }, function() { $(this).removeClass("green"); }); });
$(document).ready(function() {
$("#orderedlist li:last").hover(function() {
$(this).addClass("green");
},
function() {
$(this).removeClass("green");
});
});
و در MooTools داریم:
window.addEvent('domready',function() { $$('#orderedlist li:last-child').addEvents({ mouseenter: function() { this.addClass('green'); }, mouseleave: function() { this.removeClass('green'); } }); });
window.addEvent('domready',function() {
$$('#orderedlist li:last-child').addEvents({
mouseenter: function() {
this.addClass('green');
},
mouseleave: function() {
this.removeClass('green');
}
});
});
مجددا بسیار شبیه هستند ولی من می توانم ثابت کنم که نسخه MooTools کد بالا، بسیار روشنتر است ولی این بحث باعث طولانی تر شدن مقاله می گردد. بصورت کوتاه، کد MooTools دو رویداد تعریف کرده یکی برای ورود ماوس (mouse enter) و یکی برای خروج ماوس (mouse leave) و برای هر یک متدی مشخص کرده در حالیکه کد jQuery فشرده تر می باشد. متد hover آن دو پارامتر دریافت می کند که اولی برای ورود ماوس و دومی برای خروج ماوس اجرا می شود. من شخصا معتقدم که نمونه کد MooTools منطقی تر و گویاتر است ولی نه کاملا ملموس.
کدهای jQuery گاهی خیلی برای من مبهم می شوند. از نگاه کردن متدها اغلب چیز زیادی بدست نمی آید و تحلیل آنها مشکل است. این می تواند به این خاطر باشد که من با کدهای MooTools مأنوس تر هستم و خواندن درک آنها برای من آسانتر است. اما نکته ای که در MooTools قابل ستایش می دانم، نامگذاری مناسب متدها و کلاسهاست که تقریبا همیشه نام عمل با کارکرد آن متناظر است و کمترین شبهه در آن وجود دارد. شما در همه زبانهای برنامه نویسی باید به سراغ بخش آموزش آن بروید تا با نحوه استفاده و سینتکس کدها آشنا شوید ولی در MooTools کافی است تا با روش منسجم و پایدار APIهای آن آشنا شوید.
MooTools به شما امکان حفظ ساختارتان را می دهد
اما اگر شما به شیوه یا سینتکس jQuery علاقه داشته باشید، یک نمونه دیگر از توانایی MooTools را می توان در سادگی تغییر شیوه ها براساس میلتان بیان کرد. اگر بخواهیم متد hover در jQuery را در MooTools شبیه سازی کنیم، کافی است مشابه زیر عمل کنیم:
Element.implement({ hover : function(enter,leave){ return this.addEvents({ mouseenter : enter, mouseleave : leave }); } }); //and then you could use it exactly like the jQuery version: $$('#orderlist li:last').hover(function(){ this.addClass('green'); }, function(){ this.removeClass('green'); });
Element.implement({
hover : function(enter,leave){
return this.addEvents({ mouseenter : enter, mouseleave : leave });
}
});
//and then you could use it exactly like the jQuery version:
$$('#orderlist li:last').hover(function(){
this.addClass('green');
},
function(){
this.removeClass('green');
});
البته پلاگینهایی هم وجود دارد که سینتکس (Syntax) استاندارد jQuery را برای MooTools ایجاد می کنند. تمرکز MooTools روی توسعه آن است، به هر چیزی که مایلید. این ویژگی است که اغلب با jQuery امکانپذیر نیست. MooTools می تواند کاملا شبیه jQuery شود (خود را به شکل آن را دربیاورد) ولی jQuery توانایی تبدیل شدن به MooTools را ندارد. اگر میخواهید کلاس ایجاد کنید، اشیاء محلی را توسعه دهید و یا کارهای مشابه انجام دهید، با MooTools می توانید. این کدها را خودتان تولید کنید.
انسجام توسط یک "الگوی طراحی"
به نمونه های بیشتری توجه کنید. این یک مثال از سایت jQuery است:
$(document).ready(function() { $('#faq').find('dd').hide().end().find('dt').click(function() { $(this).next().slideToggle(); }); });
$(document).ready(function() {
$('#faq').find('dd').hide().end().find('dt').click(function() {
$(this).next().slideToggle();
});
});
این شیوه ایست که من شخصا ترجیح نمی دهم. به کد دقت کنید، فهمیدن اینکه این کدها چه کاری انجام می دهند کار آسانی نیست. بصورت دقیقتر، درک اینکه end. چه کاری انجام می دهد و find. بعد از آن چطور عمل می کند و به کدام end. مرتبط است، کمی پیچیده به نظر می آید. حالا به سراغ مستندات jQuery در سایتش برویم، آنجا بطور شفاف نحوه کارکرد end. را توضیح داده (این متد مقادیر را براساس مرجع faq# به حالت اول باز میگرداند یا موقعیت را reset می کند.). اما این برای من کمی غریب به نظر می آید. در زمان کار با jQuery، من اغلب اطمینان نداشتم که متدی خاص، چه مقداری را بر خواهد گرداند (return خواهد کرد). اما در عمل این موضوع از نظر تعداد بالایی از کاربران jQuery، اینطور به نظر نمی رسد و آنها راضی هستند پس من این مسئله را هم سلیقه ای در نظر میگیرم.
حالا به کد بالا در محیط MooTools توجه کنید:
window.addEvent('domready', function() { var faq = $('faq'); faq.getElements('dd').hide(); faq.getElements('dt').addEvent('click', function() { this.getNext().slide('toggle'); }); });
window.addEvent('domready', function() {
var faq = $('faq');
faq.getElements('dd').hide();
faq.getElements('dt').addEvent('click', function() {
this.getNext().slide('toggle');
});
});
مجددا نمونه کد با MooTools طولانی تر است ولی واضحتر نیز هست. همچنین دقت کنید که الگوی طراحی در اینجا ذخیره سازی مرجع به faq#، در یک متغیر است در حالیکه jQuery از متد end. خود برای بازگرداندن مرجع استفاده می کند. امکان ایجاد کدهای زنجیروار (chained) سطح بالا در MooTools وجود دارد (مترجم: عبارت chain برای امکان استفاده از توابع و متدها، بلافاصله بعد از هم مانند نمونه های بالا می باشد. معنای لغوی chain زنجیر است که متدها به حلقه های زنجیر که به هم متصل و بصورت متوالی می آیند تشبیه شده). به نمونه زیر نگاه کنید:
item.getElements('input[type=checkbox]') .filter(function(box) { return box.checked != checked; }) .set('checked', checked) .getParent()[(checked) ? 'addClass' : 'removeClass']('checked') .fireEvent((checked) ? 'check' : 'uncheck');
item.getElements('input[type=checkbox]')
.filter(function(box) {
return box.checked != checked;
})
.set('checked', checked)
.getParent()[(checked) ? 'addClass' : 'removeClass']('checked')
.fireEvent((checked) ? 'check' : 'uncheck');
ولی در واقع، این روش کدنویسی (وجود کدهای زیاد در رویداد domready) برای هر فریم ورکی، جالب به نظر نمی رسد. بهتر است که کدهای خود را به تکه های چندبار مصرف تبدیل کنیم.
استفاده چندباره ی کدها در jQuery
وقتی بر روی پروژه ای کار می کنید استفاده از برخی افکتها و امکانات فریم ورکها مانند انتخاب اجزاء DOM، اضافه کردن رویدادهای کلیک و mouseover و غیره بصورت کدهای معمولی بسیار وسوسه انگیز خواهد بود. ایجاد کد با این شیوه بسیار و کارآمد و سریع الوصول است. اما مشکل اصلی این شیوه یعنی نوشتن همه کدها در عبارت domready این است که بسیاری از کدها عملیات مشابه یا تکراری انجام می دهند ولی برای مکانهای متفاوت (مترجم: رویداد domready در MooTools و متد ready. در jQuery کاربردشان به این صورت است که زمانی اجرا می شوند که ساختار DOM بطور کامل در دسترس باشد بنابراین استفاده از آنها، تضمینی بر صحیح اجرا شدن کدهای مربوط به DOM است). اگر ما برای بخش FAQ (که مثالش در بالا ذکر شد) الگویی طراحی کنیم، می توانیم به جای تکرار و تولید کدها در همه صفحات، فقط از آنها استفاده کنیم. آیا روش ما برای این الگو، تکرار ساختار کدها در همه صفحات است؟
یک راه ساده برای ایجاد قابلیت چندبار مصرفی، قرار دادن کدها در یک تابع و ارسال داده ها از طریق پارامتر تابع است.
function faq(container, terms, definitions) { $(container).find(terms).hide().end().find(definitions).click(function() { $(this).next().slideToggle(); }); }; $(document).ready(function() { faq('#faq', 'dd', 'dt'); }););
این روش به دو دلیل مهم و اساسی زیر بهتر است:
- اگر در آینده بخواهیم تفییری در نحوه کارکرد کدها داشته باشیم (مثلا فعالیتهای اضافی براساس رویداد کلیک داشته باشیم یا اطلاعات را توسط AJAX دریافت کنیم) کافیست تا متد اصلی faq را تغییر دهیم تا رفتار آن در همه صفحات و بخشهای بکار گرفته شده عوض شود و یا اگر نسخه جدیدی از jQuery عرضه گردد که تغییراتی در کدها لازم باشد، بجای جستجو و تغییر انبوهی از کدها، متد اصلی به سرعت یافت و اصلاح می شود. من تلاش می کنم تا بخشی از کدها که بصورت کلی اجرا می شوند را مشخص کنم و حجمشان را تا حد امکان پایین بیاورم به این ترتیب، رفع اشکال، توسعه، بروزرسانی فریم ورک و تغییر ساختار به راحتی بیشتری انجام می گیرد.
- دلیل دوم حجم کم کدهاست. با استفاده چندباره از یک کد، مقادیری تکرار نمی گردد و در همه محیطهای برنامه نویسی قابل تغییر است همچنین بازدیدکنندگان به هنگام بارگذاری (Load) صفحه حجم کمتری را دریافت خواهند کرد.
jQuery در واقع سیستم تمیزی برای تعریف این قبیل کدهای چندبار مصرف نیست در حالیکه روش زیر به روش ساده استفاده از توابع ارجحیت دارد (و روش تابع به روش خام ارجحیت دارد). jQuery کاربران را به نگارش کدهای شخصی بصورت jQuery plug-ins تشویق می کند که در آن حالت، کد نمونه مذکور شبیه این خواهد شد:
jQuery.fn.faq = function(options) { var settings = jQuery.extend({ terms: 'dt', definitions: 'dd' }, options); //"this" is the current context; in this case, the elements we want to turn into faq layouts $(this).find(settings.terms).hide().end().find(settings.definitions).click(function() { $(this).next().slideToggle(); }); return this; };
jQuery.fn.faq = function(options) {
var settings = jQuery.extend({
terms: 'dt',
definitions: 'dd'
}, options); {
$(this).next().slideToggle();
});
return this;
};
و چیزی که استفاده خواهید کرد:
$('#faq').faq();
$('#faq').faq();
اما با کمی دقت در این شیوه و شیوه قبل، متوجه شباهت بالای آنهای خواهیم شد. با اینکه این تابع در محدوده اصلی فریم ورک (namespace) قرار ندارد ولی به سادگی می توان این کار را انجام داد (مترجم: واژه namespace به محدوده هایی اشاره دارد که متدها در آنها معتبر هستند. برای نمونه، در یک محیط، از دو متد همنام، متدی قابل اجراست که در محدوده namespace تعریف شده معتبر باشد. این محدوده ها معمولا قابل تعریف هستند). با الصاق این کدها به jQuery، ما می توانیم متدهای اصلی دیگر را همراه آن استفاده کنیم. حسن دیگر آن است که عبارت this در تابع به موقعیت فعلی که jQuery با آن روبروست اشاره دارد (مترجم: یعنی در بخش تعریف تابع، عبارت this به faq# اشاره دارد). با استفاده از این شیوه می توان کدها را به نحوی ایجاد کرد که گویی جزیی از ساختار jQuery هستند اما غیر از آن، این پلاگینها، در واقع توابع معمولی هستند که مرجع کنونی jQuery را دریافت می کنند، عملیاتی روی آن انجام می دهند و خود مرجع مورد اشاره را باز میگردانند. این حالت هیچ پیچیدگی ندارد و همه را قادر می سازد تا پلاگین تولید کنند زیرا همانطور که گفته شد، این پلاگینها در واقع همان توابع معمولی هستند که قابلیت دریافت مرجع مورد اشاره را دارند.
دقت کنید که با استفاده از دستورات و متدهای jQuery، امکان نگارش پلاگینهای پیچیده تر وجود دارد. این شیوه طراحی توسط پلاگین UI در jQuery پشتیبانی می شود و ساختار و مکانیسم مشابه آنچه ذکر شد ندارد. به جای آن ما یک شیء با همه متعلقاتش را به شیء jQuery وصل می کنیم (مانند ui.tabs.$). این یک راه کوتاه برای فراخوانی این شیء است (()selector).tabs)$) که می توان در آن اتصالات زنجیروار مانند پلاگین faq را هم داشت. اما به این علت که هیچ مرجعی بازگردانده نمی شود، شما مجبورید برای فراخوانی (invoke) متدهای شیء (در اینجا tab)، ابتدا انتخابگر (selector) را فراخوانی کنید تا متدها شناخته شوند. یعنی بجای (myTabInstance.add(url, label, index شما باید انتخابگر را اجرا کنید و نام متد را بصورت رشته به آن بدهید ;(selector).tabs('add', url, label, index)$ . این یعنی شما انتخابگر را دوبار اجرا کرده اید (حتی اگر آن را در یک متغیر ذخیره کرده باشید) و چون هیچ مرجعی برای اجرای متد add ندارید از امکاناتی مانند bind (تغییر مرجع) و delay (ایجاد وقفه) هم محرومید. این مقاله معطوف MooTools و jQuery است و با توجه به اینکه پلاگین UI بطور پیش فرض بر روی jQuery وجود ندارد، بیشتر از این بدان پرداخته نمی شود.
استفاده چندباره ی کدها در MooTools
در MooTools زمانی که قصد ایجاد یک الگو را دارید، احتمالا از Class و یا واردسازی متد به اشیاء Native (محلی) مانند String استفاده می کنید.
همچنین او شیوه یا سینتکس تقریبا متفاوتی از جاوااسکریپت را هم ارائه می دهد. MooTools تلاش می کند تا حد وسطی بین سینتکس شخصی خود و سینتکس عمومی جاوااسکریپت داشته باشد. یکی از این راهها، توسعه اشیاء محلی در این زبان یا محیط DOM است این بدین معناست که اگر مثلا به متدی برای پاکسازی (trim) یک رشته نیاز دارید، MooTools شما را تشویق می کند تا این متد را مستقیما به خود شیء String اضافه کنید (البته باید دقت شود که شیء String در MooTools، بصورت پیشفرض دارای این متد به شکل String.trim می باشد):
String.implement({ trim: function() { return this.replace(/^\s+|\s+$/g, ''); } });
String.implement({
trim: function() {
return this.replace(/^\s+|\s+$/g, '');
}
});
این یعنی، اجرای متد trim بر روی یک جمله با فاصله های اضافی، نتیجه همان جمله بدون این فاصله هاست (مترجم: یعنی ما برای شیء String یک متد جدید ایجاد کردیم که در زمان اجرا کاملا مشابه متدهای معمولی استفاده می شود). برخی شاید عنوان کنند که اضافه کردن اجزای جدید به اشیاء محلی کار مناسبی نیست. به همین دلیل است که MooTools با Prototype.js سازگاری ندارد؛ در واقع همه فریم ورکهایی که بر روی اشیاء محلی تاثیر می گذارند، قابلیت سازگاری با هم را ندارند. اگر من ()String.prototype.foo را تعریف کنم و فریم ورک دیگری هم آن را تعریف کند، متدی اجرا خواهد شد که دیرتر تعریف شده باشد مشابه همین حالت مشکلی است که ما با محدوده نام (namespace) سراسری window داریم و البته این شیوه کارکرد جاوااسکریپت است. این روشی است که جاوااسکریپت 1.8 امکانات جدیدی را از طریق افزودن prototypeها معرفی کرده.
توسعه دهندگان MooTools، فریم ورکی را طراحی کرده اند که گسترش آن برای شما آسان باشد با این قصد که کاربر فقط این فریم ورک را در صفحه وارد کند و نه فریم ورکهای دیگر را. این در واقع کمی گستاخانه خواهد بود که از کاربران خواسته شود تا مثلا دو فریم ورک را دریافت کنند. تنها دلیل این امر، نیاز به پلاگینهای موجود در هر دو فریم ورک است که توسعه دهندگان MooTools (از جمله خودم) پیشنهاد می کنیم تا زمانی را به انتقال پلاگینها با یک محیط واحد اختصاص دهید بجای اینکه از کاربران بخواهید دو فریم ورک را دریافت کنند.
زمانی که با نحوه کار جاوااسکریپت آشنا می شوید و امــکان توسعه اشیاء محلـــی را فرا می گیرید، دریچه جدیدی در برنامه نویسی را در مقابل خود خواهید دید. شما می توانید پلاگین بنویسید و مثلا در اشیاء Function، Element، Date و غیره تغییر ایجاد کنید. که البته شاید برخی معترض باشند ایـن شــیوه کدنویسی، شیوه شلوغ و نا به سامانی است و من در جواب خواهم گفت که این روش ماهیت اصلی استفاده از جاوااسکریپت است. این یک قابلیت طراحی در این زبان است. با اضافه کردن کدهای خود به اشیاء محلی، به آنها امکان کوتاه شدن و جزء جزء شدن را داده اید. jQuery هم، این چنین امکانی را دارد ولی با درنظر گرفتن محدودیتهایی در هسته اصلی خود.
در حالیکه می توانید به سادگی چندین متد از شیء jQuery را زنجیروار به هم وصل و اجرا کنید، برای اشیاء دیگر باید از روشهای عمومی بهره گیرید.اما MooTools می تواند اشیاء را تغییر دهد.
$('span.something').get('html').trim().split("\n").each(function(line){alert(line);});
با نگاه دقیق بمی توان قدرت MooTools را در تغییر prototypeها مشاهده کرد. اتصال زنجیروار (Chaining) متدها، فقط برای DOM مفید نیستند. MooTools به شما امکان این نوع اتصال را بر روی هر نوع شیء می دهد حتی قابلیت اجرای همزمان یک متد بر روی چندین بخش، نیز با استفاده این ویژگی ایجاد شده.
کلید این ویژگی در قلب MooTools با این تئوری تعریف شده که کاربر بتواند هر آنچه مایل است را ایجاد کند. اگر این قابلیت بصورت تابع و خارج از هسته می بود، شما می توانستید با توسعه آن نیازهای خود را برطرف کنید اما وظیفه هسته ایجاد همه نوع تابع برای هر نوع نیازی نیست بلکه وظیفه آن این است که ابزار ایجاد هر چه لازم دارید را فراهم کند. بخش اعظم این مقصود با امکان ساده توسعه اشیاء محلی و استفاده از مدل ارث بری prototypal ایجاد شده. تمام این کارها با جاوااسکریپت معمولی قابل اجراست ولی MooTools آنها را بسیار ساده تر و لذتبخش تر کرده است.
MooTools و "ارث بری"
تابع Class در MooTools علی رغم نامش، نه یک کلاس واقعی است و نه کلاسی را ایجاد می کند. این تابع شامل یک الگوی ظاهری شبیه به کلاس در زبانهای دیگر است و در اصل وسیله دسترسی به اشیاء و مدل ارث بری است (متاسفانه استفاده از کلمه "کلاس" مناسبترین کلمه برای تشریح این تابع است. بنابراین در این مقاله، منظور از "کلاس"، توابعیست که شیء برمی گردانند و آنهایی که "نمونه" معرفی می شوند، مشخصه های ارث برده شده(Inherit from prototype) هستند).
برای ساختن یک کلاس باید یک شیء را به متد سازنده(Constructor) تابع Class فرستاد (مترجم: متد سازنده، متدی است که در زمان نمونه گیری از کلاس، بصورت خودکار اجرا می گردد):
var Human = new Class({ initialize: function(name, age) { this.name = name; this.age = age; }, isAlive: true, energy: 1, eat: function() { this.energy = this.energy + 1; //same as this.energy++ } });
var Human = new Class({
initialize: function(name, age) {
this.name = name;
this.age = age;
},
isAlive: true,
energy: 1,
eat: function() {
this.energy = this.energy + 1; //same as this.energy++
}
});
ما در اینجا یک شیء (شیء شامل اعضایی مثل “isAlive” و “eat” و غیره) را به Class رساندیم و این شیء تبدیل به شیء اصلی همه نمونه های این کلاس شده است. برای تعریف یک نمونه باید به روش زیر عمل کرد:
var bob = new Human("bob", 20); //bob's name is "bob" and he's 20 years old.
var bob = new Human("bob", 20);.
حالا ما یک نمونه (Intstance) از Human داریم (مترجم: لازم به ذکر است که متد سازنده شیء Class، متد initialize است یعنی با نمونه گیری از هر Class، متد initialize آن خودبخود اجرا میگردد). شیء Bob مشخصه هایی که به نمونه گیری کلاس Human تنظیم شد را دریافت کرد. اما نکته مهم این است که bob از طریق ارث بری به همه مشخصه های کلاس دسترسی دارد. وقتی ارجاعی به مشخصه eat در bob داشته باشیم، این شیء دارای چنین مشخصه ای نیست. جاوااسکریپت در bob به جستجوی این متد می پردازد و آن را پیدا نمی کند پس جستجو را در ارتباطات موروثی bob که کلاس شامل شده از آن (Human) است، ادامه می دهد. این روش برای مشخصه energy هم صادق است. در نظر اول، این قابلیت جالبی به نظر نمی رسد زیرا ما علاقه نداریم مقداری که برای energy شیء bob ایجاد می کنیم، برای بقیه نمونه های Human بکار گرفته شود (مترجم: از آنجاییکه یک مشخصه از مرجع اصلی تغییر کرده، نمونه های دیگر هم که مشتق شده مرجع اصلی هستند، آن تغییر را احساس می کنند). نکته مهم این است که اولین باری که ما مقداری را برای energy در bob تعیین می کنیم، ما این مقدار را به خود bob داده ایم و نه به prototype آن (قابلیت ارث بری در اینجا، فقط برای تشخیص مشخصه ها که متعلق به کدام نمونه است، استفاده شده) پس اولین باری که متد eat اجرا می شود (مقدار energy در این متد تغییر می کند)، نتیجه مقدار تعریف شده برای خودش خواهد بود:
bob.eat(); //bob.energy == 2
bob.eat(); //bob.energy == 2
دقت کنید که مشخصه های name و age برای bob منحصر به فرد هستند. آنها در زمان نمونه گیری توسط متد خودکار تابع یعنی initialize تعریف شده اند.
چهره کلی این سبک شاید کمی برای شما ناآشنا به نظر برسد اما این همان مقادیری هستند که ما با آنها، نمونه های مجزا از یک کلاس را ایجاد می کنیم. هر نمونه (instance) موقعیت شخصی خود را دارد. اگر ما نمونه های دیگری ایجاد کنیم، هر کدام از بقیه مستقل هستند اما همه از یک منبع شامل می شوند:
var Alice = new Human(); //alice.energy == 1 //bob.energy == 2
var Alice = new Human();
//alice.energy == 1
//bob.energy == 2
جالبترین قسمت این موضوع زمانی است که این شیوه توسعه یابد.
توسعه و بکارگیری کلاسها
ابتدا نگاهی دوباره به پلاگین faq که برای jQuery ایجاد کردیم بیاندازیم. چه اتفاقی می افتد اگر بخواهیم امکانات بیشتری به آن اضافه کنیم یا چه می شود اگر بخواهیم پاسخ سوالات FAQ را توسط AJAX از سرور دریافت کنیم؟ تصور کنید که این پلاگین توسط شخص دیگری نوشته شده و ما می خواهیم بدون تغییرات اساسی در آن امکانات مورد نظرمان را اضافه کنیم (ما نمی خواهیم انسجام فعلی اش دچار اشکال شود).
تنها راههای موجود اینها است که یا منطق پلاگین faq را کپی کنیم (بخاطر بیاورد که کل منطق بصورت یک تابع بود) و تغییرات را در منطق آن اعمال کنیم و یا، آن را فراخوانی (Invoke) کنیم و وظایف جدید را به آن اضافه کنیم. گزینه دوم می تواند جلوی مشکلات زیادی را بگیرد. به این مثال توجه کنید:
jQuery.fn.ajaxFaq = function(options) { var settings = jQuery.extend({ //some ajax specific options like the url to request terms from url: '/getfaq.php' definitions: 'dd' }, options); //"this" is the current context; in this case, the elements we want to turn into faq layouts $(this).find(settings.definitions).click(function() { $(this).load(.....); //the logic to load the content from the term }); this.faq(); //call our original faq plug-in });
jQuery.fn.ajaxFaq = function(options) {
var settings = jQuery.extend({
url: '/getfaq.php'
definitions: 'dd'
}, options);
$(this).find(settings.definitions).click(function() {
$(this).load(.....); //the logic to load the content from the term
});
this.faq(); //call our original faq plug-in
});
پلاگین جدید دارای نکاتی است. اول از همه اینکه پلاگین، انتخابگری که برای definition تعیین شده را تکرار می کند زیرا راهی برای ذخیره نتیجه و استفاده مجدد از آن نیست. دوم اینکه، امکان افزودن بخش مربوط به AJAX در قسمتی که ما اضافه کردیم (بخش پایینی) وجود ندارد و این کدها باید وارد منطق اصلی پلاگین شود. پلاگین اصلی از slideToggle که بخش definition را با افکت اسلاید نمایش می دهد، استفاده می کند و این، نکته ی مشکل ساز توسعه ماست زیرا، بخش definition در مدل ما توسط AJAX دریافت می شود. وقتی موردی از faq کلیک شود، افکت slideToggle بلافاصله فعال می گردد و definition را نمایش می دهد حتی اگر هنوز دریافت محتوای آن توسط AJAX به اتمام نرسیده باشد (به بیان دیگر، قاعده صحیح به این صورت است که با کلیک، AJAX فعال گردد و وقتی نتیجه AJAX آماده شد، افکت اسلاید برای نتیجه اعمال شود). پس این افکت باید حذف گردد چون متاسفانه هیچ راه حلی برای چنین مشکلی وجود ندارد مگر تکرار و کپی (duplicate) کل پلاگین.
حال به کلاس Human که تحت MooTools ایجاد کردیم دقت کنید. او مشخصه های مثل isAlive و energy، و متدی با نام eat دارد. برای اضافه کردن مشخصه های دیگر به این کلاس چه کار باید کرد؟ در MooTools ما کلاس را اینگونه توسعه (Extend) می دهیم:
var Ninja = new Class({ Extends: Human, initialize: function(name, age, side) { this.side = side; this.parent(name, age); }, energy: 100, attack: function(target) { this.energy = this.energy - 5; target.isAlive = false; } });
var Ninja = new Class({
Extends: Human,
initialize: function(name, age, side) {
this.side = side;
this.parent(name, age);
},
energy: 100,
attack: function(target) {
this.energy = this.energy - 5;
target.isAlive = false;
}
});
می بینید که ما به کلاس جدید وظایف زیادی اضافه کردیم. کلاس جدید همه مشخصه های موجود را برای خودش یعنی کلاس Ninja دارد. Ninja مقدار energy را با 100 آغاز می کند و مشخصه جدیدی به نام side هم دارد. همچنین او می تواند از طریق متد attack خود، Human های دیگر را نابود کند و در این راه مقداری انرژی از دست دهد.
var bob = new Human('Bob', 25); var blackNinja = new Ninja('Nin Tendo', 'unknown', 'evil'); //blackNinja.isAlive = true //blackNinja.name = 'Nin Tendo' blackNinja.attack(bob); //bob never had a chance
var bob = new Human('Bob', 25);
var blackNinja = new Ninja('Nin Tendo', 'unknown', 'evil');
//blackNinja.isAlive = true
//blackNinja.name = 'Nin Tendo'
blackNinja.attack(bob);
//bob never had a chance
جزییات جالب زیادی در این مثال وجود دارد. قابل توجه است که کلاس Ninja متد initialize مربوط به خود را دارد؛ این متد، متد مشابه در کلاس والد (اینجا Human) را رونویسی (overwrite) می کند ولی همچنان می توان از طریق this.parent، متد initialize کلاس والد را اجرا کرد. طبیعتا لازم است تا پارامترهای لازم برای اجرای متد initialize والد را در this.parent قرار داد. به علاوه می توانیم منطق کلاس را، قبل و بعد از فراخوانی parent کنترل کنیم. ما قادریم مقادیر جدیدی به مشخصه ها بدهیم (مثلا به energy) و وظایف جدید اضافه کنیم (قبل و بعد از اجرای this.parent). تصور کنید چقدر خوب می شد که می توانستیم این خاصیت را در پلاگین faq در jQuery وارد کنیم. حالا با این قابلیت می توانیم در این پلاگین، ابتدا محتوای AJAX را کامل دریافت کنیم و سپس افکت اسلاید را داشته باشیم.
MooTools یک الگوی دیگر با نام Mixin دارد. درحالیکه روش extend رابطه موروثی بین کلاس والد و فرزند ایجاد می کند، شما قادرید تا کلاسهایی تعریف کنید تا با کلاسهای دیگر ادغام شوند و مشخصه های خود را منتقل کنند. برای مثال:
var Warrior = new Class({ energy: 100, kills: 0, attack: function(target) { target.isAlive = false; this.energy = this.energy - 5; this.kills++; } });
var Warrior = new Class({
energy: 100,
kills: 0,
attack: function(target) {
target.isAlive = false;
this.energy = this.energy - 5;
this.kills++;
}
});
اینجا ما صفات را تغییر دادیم تا کلاس Ninja را از کلاس Human متمایز کنیم و صفات را در کلاس خودشان قرار دهیم. این به ما امکان استفاده مجدد این کدها را، خارج از کلاس Ninja می دهد. حال ما می توانیم کلاس Ninja را با صفات Warrior ادغام کنیم. یعنی:
var Ninja = new Class({ Extends: Human, Implements: Warrior, //can be an array if you want to implement more than one initialize: function(name, age, side) { this.side = side; this.parent(name, age); } });
var Ninja = new Class({
Extends: Human,
Implements: Warrior,
initialize: function(name, age, side) {
this.side = side;
this.parent(name, age);
}
});
تفاوتی در عملکرد Ninja بوجود نیامد فقط در این روش، ما Warrior را برای ایجاد خاصیت استفاده مجدد، اضافه کردیم.
var Samurai = new Class({ Extends: Human, Implements: Warrior, side: 'good' });
var Samurai = new Class({
Extends: Human,
Implements: Warrior,
side: 'good'
});
حالا ما یک کلاس Samurai و یک کلاس Ninja داریم اما به کوتاهی کدهایی که صرف تعریف این دو کلاس شده دقت کنید. هر دو از نظر داشتن صفات Human و Warrior مشترک هستند ولی Samurai همیشه دارای مقدار good برای مشخصه side است و Ninja اینطور نیست. با صرف زمان برای طراحی کلاسهای Human و Warrior، ما سه کلاس متفاوت بدون تکرار اضافی کد داریم که دارای قابلیت کنترل بصورت جزء جزء هستند؛ بطوریکه وقتی متدها فراخوانی می شوند این کلاسها با یکدیگر در ارتباط قرار می گیرند. هر نمونه (Instance) محدوده مختص به خود را دارد و کدها کاملا واضح و خوانا هستند.
در حال حاضر ما اطلاعاتی درباره اینکه کلاسهای MooTools چطور فعالیت می کنند داریم. اجازه بدهید تا به پلاگین faq که برای jQuery ایجاد کردیم و می خواستیم امکانات AJAX را به آن اضافه کنیم، بپردازیم و ببینیم که MooTools در ساختن نمونه مشابه چطور عمل می کند:
var FAQ = new Class({ //Options is another class provided by MooTools Implements: Options, //these are the default options options: { terms: 'dt', definitions: 'dd' }, initialize: function(container, options) { //we store a reference to our container this.container = $(container); //setOptions is a method provided by the Options mixin //it merges the options passed in with the defaults this.setOptions(options); //we store the terms and definitions this.terms = this.container.getElements(this.options.terms); this.definitions = this.container.getElements(this.options.definitions); //we call our attach method //by breaking this into its own method //it makes our class easier to extend this.attach(); }, attach: function(){ //loop through the terms this.terms.each(function(term, index) { //add a click event to each one term.addEvent('click', function(){ //that calls our toggle method for //the current index this.toggle(index); }, this); }, this); }, toggle: function(index){ //toggle open the definition for the given index this.definitions[index].slide('toggle'); } });
var FAQ = new Class({
//Options is another class provided by MooTools
Implements: Options,
//these are the default options
options: {
terms: 'dt',
definitions: 'dd'
},
initialize: function(container, options) {
//we store a reference to our container
this.container = $(container);
//setOptions is a method provided by the Options mixin
//it merges the options passed in with the defaults
this.setOptions(options);
//we store the terms and definitions
this.terms = this.container.getElements(this.options.terms);
//we call our attach method
//by breaking this into its own method
//it makes our class easier to extend
this.attach();
},
attach: function(){
//loop through the terms
this.terms.each(function(term, index) {
//add a click event to each one
term.addEvent('click', function(){
//that calls our toggle method for
//the current index
this.toggle(index);
}, this);
}, this);
},
toggle: function(index){
//toggle open the definition for the given index
this.definitions[index].slide('toggle');
}
});
باز هم مقدار کدها بیشتر از نمونه jQuery شد. اگر ما همه خطهای توضیحی (comment lines) را حذف کنیم باز هم این پلاگین حدود دوازده خط بیشتر دارد. البته من می توانم ثابت کنم که چنین پلاگینی را می توان با تقریبا همین اندازه برای jQuery ایجاد کرد ولی به هر حال چرا این روش طولانی تر است؟ پاسخ اینطور خواهد بود که این کلاس بسیار انعطاف پذیرتر است. برای استفاده از کلاس، کافیست متد سازنده آن اجرا شود:
var myFAQ = new FAQ(myContainer); //and now we can call methods on it if we want: myFAQ.toggle(2); //toggle the 3rd element
var myFAQ = new FAQ(myContainer);
//and now we can call methods on it if we want:
myFAQ.toggle(2); //toggle the 3rd element
ما می توانیم به همه متدها و مشخصه ها دسترسی داشته باشیم اما درباره AJAX چه باید کرد؟ مشکل ما با AJAX در پلاگین jQuery این بود که نمی توانستیم، افکت اسلاید را بعد از اتمام عملیات AJAX اجرا کنیم ولی در پلاگین MooTools، چنین مشکلی نداریم:
FAQ.Ajax = new Class({ //this class inherits the properties of FAQ Extends: FAQ, //it also gets a new option in addition to the other defaults //this one for url, that we're going to append the index of the //term to; in reality we might make this more robust, but for //this example it serves the purpose options: { url: null; }, //we're going to cache the results, so if a section is opened //twice, we won't hit the server for the data indexesLoaded: [], toggle: function(index){ //if we've already loaded the definition if (this.indexesLoaded[index]) { //just call the previous version of toggle this.parent(index); } else { //otherwise, request the data from the server new Request.HTML({ update: this.definitions[index], url: this.options.url + index, //and when the data is loaded, expand the definition onComplete: function(){ this.indexesLoaded[index] = true; this.definitions[index].slide('toggle'); }.bind(this) }).send(); } } });
FAQ.Ajax = new Class({
//this class inherits the properties of FAQ
Extends: FAQ,
//this example it serves the purpose
options: {
url: null;
},
//twice, we won't hit the server for the data
indexesLoaded: [],
toggle: function(index){
//if we've already loaded the definition
if (this.indexesLoaded[index]) {
//just call the previous version of toggle
this.parent(index);
} else {
//otherwise, request the data from the server
new Request.HTML({
update: this.definitions[index],
url: this.options.url + index,
//and when the data is loaded, expand the definition
onComplete: function(){
this.indexesLoaded[index] = true;
this.definitions[index].slide('toggle');
}.bind(this)
}).send();
}
}
});
حالا ما نسخه ای از کلاس FAQ داریم که اطلاعات را از سرور دریافت می کند. دقت کنید در این روش امکان استفاده از اسلاید بعد از بارگذاری کامل فراهم شد (کاری که در jQuery ممکن نشد) همچنین مدنظر داشته باشید که امکان اضافه شده جدید (AJAX) با تغییرات کمی در منطق اصلی صورت گرفت. این مدل توسعه ای، امکان ایجاد مجموعه ای از پلاگینهای مرتبط به هم ولی متفاوت را به سادگی فراهم می کند. همچنین شما قادر می شوید تا با تغییر اندک (نه اساسی) پلاگین های آماده دیگر را برای نیاز خود شخصی کنید. این پاسخ به این ابهام است که چرا وقتی در جستجوی پلاگین هایی مثل date picker یا tab interface برای MooTools هستید، با نتایج کمی روبرو می شوید. علت این است که اکثر پلاگین های آماده یا مشکل شما را کامل حل خواهند کرد یا این منظور با کمی تغییر و توسعه حاصل می شود.
همانطور که قبلا تاکید کردم، طراحی مجموعه ای از ابزارها و امکانات توسط jQuery امکانپذیر است ولی اکثر کدهایی که برای این منظور نوشته می شوند، ارتباطی با DOM (بخش تسلط jQuery) ندارد و کدهای معمولی جاوااسکریپت خواهد بود. اما مدل jQuery، برای توسعه این نمونه ها به کلاسهای زیرمجموعه، و برای مدل Mixin که استفاده چندباره کدها را آسان می کند، روشی را معرفی نمی کند. و در آخر اینکه پلاگینهای jQuery همیشه برای اجزاء DOM کار می کنند و اگر لازم باشد تا پردازش بر روی چیزی خارج از DOM مثل یک URL انجام گیرد، باید کدهای آن را خودتان بنویسید.
زمان انتخاب
jQuery بر روی قابل فهم بودن و سادگی و سرعت کدنویسی برای DOM تمرکز دارد و MooTools بر روی توسعه، ارث بری، خوانایی، قابلیت استفاده چندباره کدها و خصوصیت حمل بالا تاکید دارد (مترجم: خصوصیت حمل در برنامه نویسی، به امکان کار بر روی کدها – چه استفاده، چه توسعه – در محیطهای مختلف، با شرایط مختلف اطلاق می گردد). اگر این دو فریم ورک را در دو کفه ترازو قرار دهیم، کفه jQuery را می توان اینگونه معرفی کرد: قابلیت فراگیری آسان و مشاهده فوری نتایج اما (براساس تجربیات من) قابلیت کم چندبار مصرفی و عدم حمل بالا (البته این کاملا به شما بستگی دارد و فی نفسه مشکل jQuery محسوب نمی گردد) در حالیکه کفه MooTools بیانگر این خصوصیات است: یادگیری دشوارتر، صرف زمان و کدنویسی بیشتر برای حصول نتایج، قابلیت استفاده چندباره و حمل بالا.
به علاوه هسته MooTools همه قابلیتهایی که در تصور شماست یا در هسته jQuery موجود است را شامل نمی شود. هر دو فریم ورک ترجیح بر تولید پلاگینها و توسعه هایی دارند که توسط شما و سایر برنامه نویسان ایجاد می شود. وظیفه آنها فراهم کردن هر امکانی که شما نیاز دارید، نیست بلکه با ایجاد ابزاری برای تولید امکانات متفاوت، شما را به طراحی آنها ترغیب می کنند. بطورکلی این قدرت جاوااسکریپت و فریم ورکهای آن است که MooTools و jQuery در حصول آن تلاش می کنند. MooTools دسترسی کلی تری را فراهم کرده و ابزار نگارش هر قابلیتی را به شما می دهد حتی فراتر از محدوده DOM اما تلاش بیشتری را برای یادگــیری و تســـلط می طلبد. دید کلی و توسعه پذیری MooTools، سطح قابل توجهی از امکانات jQuery را تحت قلمرو خود قرار داده. اما jQuery تمرکز و فوکوس خود را معطوف توابع و APIهای جذاب DOM کرده اما در عین حال مثل MooTools شما را از توسعه اشیاء محلی و ارث بری در جاوااسکریپت منع نکرده است.
این دقیقا دلیل این گفته من است که هر کدام از آنها، به تنهایی گزینه کاملی هستند. تلاش من در این مقاله بیان تفاوتهای موجود در فلسفه کدهای آنها و نشان دادن مزایا و معایب این دو فریم ورک بود. من از موفقیت در دخیل نکردن سلیقه شخصی نزدیک به MooTools خود، در این مقاله مطمئن نیستم؛ اما امیدوارم که مقاله سودمندی بوده باشد. امیدوارم، صرفنظر از اینکه چه فریم ورکی را انتخاب می کنید، هر دوی آنها را بخوبی شناخته باشید. اگر وقت آزاد داریــد، من به شــدت توصیـــه می کنم تا سایتی را با هر یک از آنها ایجاد کنید. نقطه نظر شما از انجام یک پروژه با دو فریم ورک می تواند، نکته ای که من از قلم انداخته ام را به این مقاله بیافزاید.
نوشته شده توسط Aaron Newton در مه 2009
ترجمه شده توسط امیرحسین حجتی پور در تیر ماه 1388
منبع: http://jqueryvsmootools.com/index_fa.html