استفاده از NuGet Package Manager برای مدیریت وابستگیها
درباره اهمیت NuGet برای مصرف کنندگان قبلا این مطلب نوشته شده است.
بجای صرف وقت برای اینکه بررسی کنیم آیا این نسخهی جدید کتابخانهی X یا اسکریپت jQuery آمده است یا خیر، میتوان این وظیفه را به NuGet سپرد. علاوه بر این NuGet مزیتهای دیگری هم دارد؛ مثلا تیمهای برنامه نویسی میتوانند کتاب خانههای مشترک خودشان را در مخزنهای سفارشی NuGet قرار دهند و توزیع و Versioning آنرا به NuGet بسپارند.
تکیه بر Abstraction (انتزاع)
Abstraction در طراحی سیستمها منجر به تولید نرم افزار هایی Loosely coupled با قابلیت نگهداری بالا و همچنین فراهم شدن زمینه برای نوشتن Unit Test میشود.
اگر به مطالب قبلی وب سایت برگردیم در مطلب چرا ASP.NET MVC گفته شد که :
2) دستیابی به کنترل بیشتر بر روی اجزای فریم ورک :
در طراحی ASP.NET MVC همهجا interfaceها قابل مشاهد هستند. همین مساله به معنای افزونه پذیری اکثر قطعات تشکیل دهنده ASP.NET MVC است؛ برخلاف ASP.NET web forms. برای مثال تابحال چندین view engine، routing engine و غیره توسط برنامه نویسهای مستقل برای ASP.NET MVC طراحی شدهاند که هیچکدام با ASP.NET web forms میسر نیست. برای مثال از view engine پیش فرض آن خوشتان نمیآید؟ عوضش کنید! سیستم اعتبار سنجی توکار آنرا دوست ندارید؟ آنرا با یک نمونه بهتر تعویض کنید و الی آخر ...
به علاوه طراحی بر اساس interfaceها یک مزیت دیگر را هم به همراه دارد و آن هم ساده سازی mocking (تقلید) آنها است جهت ساده سازی نوشتن آزمونهای واحد.
از کلمهی کلیدی New استفاده نکنید
هر جا ممکن است کار وهله سازی اشیاء را به لایه و حتی Framework دیگری بسپارید. هر زمان اشیاء نرم افزار خودتان را با کلمهی new وهله سازی میکنید اصل Abstraction را فراموش کرده اید. هر زمان اشیاء نرم افزار را مستقیم وهله سازی میکنید در نظر داشته باشید میتوانید آنها را به صورت وابستگی تزریق کنید.
در همین رابطه مطالب زیر را از دست ندهید :
- تزریق وابستگی (dependency injection) به زبان ساده
- تزریق وابستگی (Dependency Injection) و توسعه پذیری
از HttpContext مستقیما استفاده نکنید (از HttpContextBase استفاده کنید)
از .NET 4 به بعد فضای نامی تعریف شده که در بر دارندهی کلاسهای انتزاعی (Abstraction) خیلی از قسمتهای اصلی ASP.NET است. یکی از مواردی که در توسعهی ASP.NET معمولا زیاد استفاده میشود، شیء HttpContext است . استفاده از HttpContextBase را به استفاده از HttpContext ترجیح دهید. اهمیت این موضوع در راستای اهمیت انتزاع (Abstraction) میباشد.
از "رشتههای جادویی" اجتناب کنید
استفاده از رشتههای جادویی در خیلی از جاها کار را ساده میکند؛ بعضی وقتها هم به آنها نیاز است اما مشکلات زیادی دارند :
- رشتهها معنای باطنی ندارند (مثلا : دشوار است که از روی نام یک ID مشخص کنم این ID چطور به ID دیگری مرتبط است و یا اصلا ربط دارد یا خیر)
- با اشتباهات املایی یا عدم رعایت حروف بزرگ و کوچک ایجاد مشکل میکنند.
- به Refactoring واکنش خوبی نشان نمیدهند. (برای درک بهتر این مطلب را بخوانید.)
<p> <label for="FirstName">First Name:</label> <span id="FirstName">@ViewData["FirstName"]</span> </p>
<p> <label for="FirstName">First Name:</label> <span id="FirstName">@Model.FirstName</span> </p>
در رابطه با Magic strings این مطلب را مطالعه بفرمایید.
از نوشتن HTML در کدهای "Backend" خودداری کنید
با توجه به اصل جداسازی وابستگیها (Separation of Concerns) وظیفهی کنترلرها و دیگر کدهای backend رندر کردن HTML نیست. (ساده سازی کنترلر ها) البته در نظر داشته باشید که قطعا تولید HTML در متدهای کمکی کلاس هایی که "تنها" وظیفهی آنها کمک به Viewها جهت تولید کد هست ایرادی ندارد. این کلاسها بخشی از View در نظر گرفته میشوند نه کدهای "backend".
در Viewها "Business logic" انجام ندهید
معکوس بند قبلی هم کاملا صدق میکند ، منظور این است که Viewها تا جایی که ممکن است باید حاوی کمترین Business logic ممکن باشند. در واقع تمرکز Viewها باید استفاده و نحوهی نمایش داده ای که برای آنها فراهم شده باشد نه انجام عملیات روی آن.
استفاده از Presentation Model را به استفاده مستقیم از Business Objectها ترجیح دهید
در مطالب مختلف وب سایت اشاره به اهمین ViewModelها شده است. برای اطلاعات بیشتر بند ج آموزش 11 از سری آموزشهای ASP.NET MVC را مطالعه بفرمایید.
Ifهای شرطی را در Viewها را در متدهای کمکی کپسوله کنید
استفاده از شرطها در View کار توسعه دهنده را برای یک سری اعمال ساده میکند اما میتواند باعث کمی کثیف کاری هم شود. مثلا:
@if(Model.IsAnonymousUser) { <img src="@Url.Content("~/content/images/anonymous.jpg")" /> } else if(Model.IsAdministrator) { <img src="@Url.Content("~/content/images/administrator.jpg")" /> } else if(Model.Membership == Membership.Standard) { <img src="@Url.Content("~/content/images/member.jpg")" /> } else if(Model.Membership == Membership.Preferred) { <img src="@Url.Content("~/content/images/preferred_member.jpg")" /> }
public static string UserAvatar(this HtmlHelper<User> helper) { var user = helper.ViewData.Model; string avatarFilename = "anonymous.jpg"; if (user.IsAnonymousUser) { avatarFilename = "anonymous.jpg"; } else if (user.IsAdministrator) { avatarFilename = "administrator.jpg"; } else if (user.Membership == Membership.Standard) { avatarFilename = "member.jpg"; } else if (user.Membership == Membership.Preferred) { avatarFilename = "preferred_member.jpg"; } var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); var contentPath = string.Format("~/content/images/{0}", avatarFilename) string imageUrl = urlHelper.Content(contentPath); return string.Format("<img src='{0}' />", imageUrl); }
@Html.UserAvatar()
منتظر قسمت بعدی باشید.
بعد از خوندن مطلب ، اول فکر کردم که استفاده از jQuery UI ضروری نیست و فقط در صورتی که بخوایم امکان autoComplete رو فعال کنیم باید رفرنسی به اون بدیم اما بعد از حذف کردن متوجه شدم که اینطور نیست و کلا این plugin هم به jQuery و هم به jQuery UI وابسته است و بدون UI کار نمیکنه .
پلاگین jQuery جهت فیلتر کردن اطلاعات و استفاده در گزارش سازهای تحت وب ( پیش نمایش )
(jui_filter_rules is a jQuery Data filtering plugin (query builder
مدل دیگری از آن افزونهی jQuery QueryBuilder میباشد
رفع مشکلات:
در قسمت قبل با ذکر یک مثال و بیان مشکلات آن از دیدگاه اصول Defensive Code قصد داشتیم که مساله را روشنتر کنیم. مواردی که در
قسمت قبل ذکر شدند، به سادهترین شکل ممکن بیان
شدند و شما به راحتی با بررسی این موارد و تفکر در کدهای خود، میتوانید
این موارد را در کدی که خودتان مینویسید رعایت کنید. حل پیچیدگیهای موجود در کد قبل، با در نظر گرفتن اصول مذکور و اصولهای طراحی مختلف میتواند به روشهای مختلفی انجام گیرد. برای مثال میتوان برای هر یک از کارهایی که کد مثال قبل انجام میدهد، یک کلاس مجزا ایجاد نمود و اصول مذکور را در آن رعایت کرد. درنهایت این
کلاسها را در قالب یک Class Library دسته بندی کرد.
Predictability:
در ادامه قصد داریم در مورد قابلیت پیش بینی و فواید رعایت اصول آن در کد، بحث کنیم. به صورت کلی یک متد، یکسری پارامترها را به عنوان ورودی دریافت میکند؛ یک عملیات خاص را بر روی پارامترهای ورودی انجام میدهد و در نهایت، در صورت لزوم یک مقدار را بر میگرداند. پارامترهای ورودی میتوانند از سمت کاربر و از یک سورس خارجی وارد شوند و یا میتوانند از یک متد دیگر به این متد ارسال شوند. اولین مرحله برای ایجاد قابلیت Predictability این است که یکسری گاردها را به کد خود اضافه کنید تا به وسیلهی آنها پارامترهای ورودی را بررسی کنید و فقط اجازهی ورود، ورودیهای معتبر را بدهیم. برای مثال کد ذیل را درنظر بگیرد.
پارامترهای ورودی این کد با مستطیل قرمز رنگ مشخص شده اند. حال ما سعی داریم با قرار دادن یکسری گارد برای پارامترهای ورودی، از ورود مقادیر ناخواسته جلوگیری کنیم.
بر اساس اصول (GIGO (Garbage in-Garbage out در برنامه نویسی متدی که ورودیهای نامعتبر به آن پاس داده شوند، خروجیهای نامعتبری هم پس خواهد داد. بنابراین برای جلوگیری از این مسئله باید از ورود ورودیهای نامعتبر به متدها جلوگیری کرد. گاردها از ورود مقادیر نامعتبر به متدها جلوگیری خواهند کرد و در نتیجه خروجی مناسب و قابل پیش بینی از متد گرفته خواهد شد. برای جلوگیری از ورود دادههای نامعتبر، باید با استفاده از این دستورات که در ابتدای متد قرار داده میشوند، از ورود دادههای نامعتبر جلوگیری کرد. به این دستورات Guard Clauses گفته میشود. غیر از این مساله، کاهش دادن تعداد پارامترها و قراردادن قانونی برای تعیین اولویت پارامترهای متدها (برای مثال با توجه به اهمیت) میتواند به افزایش Predictability متدها بسیار کمک کند. با پیروی کردن از این اصول ساده شما میتوانید میزان خطاهایی که از پارامترهای ورودی منشاء میگیرند را کاهش دهید.
اجازه دهید با یک مثال؛ مسالهی بالا را تشریح کنیم. برای مثال یک برنامهی کوچک نوشتهایم؛ برای شمردن گام ها. در این برنامه تعداد قدمهای هدف و تعداد قدمهای برداشته شدهی امروز تعیین میشوند و سپس هدف، بر حسب درصد بیان خواهد شد.
با استفاده از این Application میخواهیم مفاهیمی را که بیان کردیم، به صورت کاربردی
نمایش دهیم. کدی این محاسبه را برای ما انجام میدهد، در ذیل نمایش داده شده و
در قالب یک متد تعیین شده است.
private decimal CalculatePercentOfGoalSteps (string goalSteps, string actualSteps) { return (Convert.ToDecimal(actualSteps) / Convert.ToDecimal(goalSteps)) * 100; }
این متد دارای دو پارامتر از نوع string می باشد و نتیجه هم در قالب یک مقدار decimal بازگشت داده خواهد شد. این جمله کلیتی از متد را بیان خواهد کرد. نحوهی فراخوانی این متد هم در کد ذیل آورده شد است.
private void Calculate_Click(object sender, EventArgs e) { var result =CalculatePercentOfGoalSteps (stepGoalForTodayTxt.Text, numberOfStepsForToday.Text); lblResult.Text = "شما به" + result + "% از هدف تان رسیده اید"; }
حال Application را اجرا کرده و نتیجه کار را مشاهده میکنیم. برای مثال شکل ذیل:
در این مثال با توجه به مقادیر وارد شده، به 40 درصد از هدف مورد نظر رسیدهایم. اما هدف از بیان این مثال، این نیست که مشخص گردد که ما چقدر به هدفمان نزدیک شدهایم. بلکه هدف مسایل دیگری است. در نظر بگیرید که بجای 5000، صفر را وارد کنید. در این حالت با یک Exception روبرو میشویم:
همانطور که در شکل بالا مشاهده میکنید، خطای Divide by zero رخ داده است. برای رفع این خطا و جلوگیری از رخداد این خطا، میتوان کد
ذیل را پیشنهاد داد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal result =0; var goalStepsCount = Convert.ToDecimal(goalSteps); if (goalStepsCount>0) { result = (Convert.ToDecimal(actualSteps) / goalStepsCount) * 100; } return result; }
با تغییر کد به این صورت مشکل Exception بالا حل میشود، اما باز هم مشکل دیگری وجود دارد. فرض کنید همانند شکل ذیل textbox اول را خالی کنیم و بعد از آن سعی در محاسبه داشته باشیم،
باز هم یک Exception دیگر
علت بوجود آمدن این مشکل این است که ما در کد امکان خالی بودن پارامترهای متد را در نظر نگرفتهایم و پیش بینیهای لازم صورت نگرفته است بنابراین دستور Convert .با مشکل مواجه شد. برای حل این مشکل میتوان به جای Convert از decimal.Tryparse استفاده کرد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal result = 0; decimal goalStepsCount = 0; decimal.TryParse(goalSteps, out goalStepsCount); decimal actualStepsCount = 0; decimal.TryParse(actualSteps, out actualStepsCount); if (goalStepsCount>0) { result = (actualStepsCount / goalStepsCount) * 100; } return result; }
با انجام دادن این کارها از بروز خطاهایی که ناشی از ورودیهای نامعتبر در کد هستند، جلوگیری کردیم. اما آیا این پایان کار است؟ خیر با استفاده کردن از این روش ما توانستهایم که از بروز خطا در برنامه جلوگیری کنیم. اما مشکلی که این روش دارد این است که کاربر متوجه نمیشود که چه زمانی برنامه دچار مشکل شده است. کاری که ما انجام میدهیم این است که برای تمامی حالات خطا، مقدار صفر را بر میگردانیم.
برای اینکه بتوانیم این کد به راحتی debug کنیم باید از مفهوم Fail Fast استفاده کنیم . این مفهوم قابلیتی را در کد ایجاد میکند که در صورتی که کد، دادههای نامعتبری را دریافت کرد، سریعا اجرای آن متوقف میشود و همزمان نیز اطلاعاتی در مورد خطا در اختیار کاربر قرار میدهد. برای این منظور با قرار دادن یکسری Guard Clauses، کد بالا را همانند شکل ذیل تغییر خواهیم داد.
private decimal CalculatePercentOfGoalSteps(string goalSteps, string actualSteps) { decimal goalStepsCount = 0; decimal actualStepsCount = 0; /// اطمینان حاصل میکنند که پارامترهای ورودی دارای مقدار هستند if (string.IsNullOrWhiteSpace(goalSteps)) throw new ArgumentException("مقدار هدف باید وارد شود", "goalSteps"); if (string.IsNullOrWhiteSpace(actualSteps)) throw new ArgumentException("مقدار واقعی باید وارد شود", "goalSteps"); ///اطمینان حاصل میکنند که مقادیر وارد شده حتما عددی هستند if (!decimal.TryParse(goalSteps, out goalStepsCount)) throw new ArgumentException("مقدار هدف باید عددی باشد", goalSteps); if(!decimal.TryParse(actualSteps, out actualStepsCount)) throw new ArgumentException("مقدار واقعی باید عددی باشد", actualSteps); ///اطمینان حاصل میکند که مقدار متغیر نباید صفر باشد if (goalStepsCount <= 0) throw new ArgumentException("مقدار هدف نباید صفر و یا کمتر از صفر باشد", "goalStepsCount"); return (actualStepsCount / goalStepsCount) * 100; }
ایجاد کردن این تغییرات در متد باعث افزایش خوانایی
کد میشود و هدف متد را روشنتر بیان خواهد کرد. اضافه کردن این کدها به دلیل اینکه
تمامی شرایط تست را تعیین خواهیم کرد Test-ability کد را بالا میبرد. اضافه کردن کدهای بالا به برنامه کمک خواهد کرد که
شرایط خطا در برنامه به درستی هندل شود و به طبع آن تصمیمات مناسبی گرفته شود و در
نهایت Predictability متدها و کل برنامه را افزایش میهد.
public class Rectangle { public double Width; public double Height; public double Area() { return Width*Height; } public double Perimeter() { return 2*(Width + Height); } }
تذکر: ممکن است این سوال پیش بیاید که خوب ما کلاس را نوشته ایم و خودمان میدانیم چه مقادیری برای فیلدهای آن مناسب است. اما مسئله اینجاست که اولاً ممکن است کلاس تهیه شده توسط برنامه نویس دیگری مورد استفاده قرار گیرد. یا حتی پس از مدتی فراموش کنیم چه مقادیری برای کلاسی که مدتی قبل تهیه کردیم مناسب است. و از همه مهمتر این است که کلاسها و اشیاء به عنوان ابزاری برای حل مسائل هستند و ممکن است مقادیری که به فیلدها اختصاص مییابد در زمان نوشتن برنامه مشخص نباشد و در زمان اجرای برنامه توسط کاربر یا کدهای بخشهای دیگر برنامه تعیین گردد.
به طور کلی هر چه کنترل و نظارت بیشتری بر روی مقادیر انتسابی به اشیاء داشته باشیم برنامه بهتر کار میکند و کمتر دچار خطاهای مهلک و بدتر از آن خطاهای منطقی میگردد. بنابراین باید ساز و کار این نظارت را در کلاس تعریف نماییم.
برای کلاسها یک نوع عضو دیگر هم میتوان تعریف کرد که دارای این ساز و کار نظارتی است. این عضو Property نام دارد و یک مکانیسم انعطاف پذیر برای خواندن، نوشتن یا حتی محاسبه مقدار یک فیلد خصوصی فراهم مینماید.
تا اینجا باید به این نتیجه رسیده باشید که تعریف یک متغیر با سطح دسترسی عمومی در کلاس روش پسندیده و قابل توصیه ای نیست. بنابراین متغیرها را در سطح کلاس به صورت خصوصی تعریف میکنیم و از طریق تعریف Property امکان استفاده از آنها در بیرون کلاس را ایجاد میکنیم.
حال به چگونگی تعریف Propertyها دقت کنید.
public class Rectangle { private double _width = 0; private double _height = 0; public double Width { get { return _width; } set { if (value > 0) { _width = value; } } } public double Height { get { return _height; } set { if (value > 0) { _height = value; } } } public double Area() { return _width * _height; } public double Perimeter() { return 2*(_width + _height); } }
دو قسمت اضافه شده دیگر تعریف دو Property مورد نظر است. یکی عرض و دیگری ارتفاع. خط اول تعریف پروپرتی تفاوتی با تعریف فیلد عمومی ندارد. اما همان طور که میبینید هر فیلد دارای یک بدنه است که با {} مشخص میشود. در این بدنه ساز و کار نظارتی تعریف میشود.
نحوه دسترسی به پروپرتیها مشابه فیلدهای عمومی است. اما پروپرتیها در حقیقت متدهای ویژه ای به نام اکسسور (Accessor) هستند که از طرفی سادگی استفاده از متغیرها را به ارمغان میآورند و از طرف دیگر دربردارنده امنیت و انعطاف پذیری متدها هستند. یعنی در عین حال که روشی عمومی برای داد و ستد مقادیر ارایه میدهند، کد پیاده سازی یا وارسی اطلاعات را مخفی نموده و استفاده کننده کلاس را با آن درگیر نمیکنند. قطعه کد زیر چگونگی استفاده از پروپرتی را نشان میدهد.
Rectangle rectangle = new Rectangle(); rectangle.Width = 10; Console.WriteLine(rectangle.Width);
تذکر: به دو اکسسور get و set مانند دو متد معمولی نگاه کنید از این نظر که میتوانید در بدنه آنها اعمال دلخواه دیگری بجز ذخیره و بازیابی اطلاعات پروپرتی را نیز انجام دهید.
چند نکته:
- اکسسور get هنگام بازگشت یا خواندن مقدار پروپرتی اجرا میشود و اکسسور set زمان انتساب یک مقدار جدید به پروپرتی فراخوانی میشود. جالب آنکه در صورت لزوم این دو اکسسور میتوانند دارای سطوح دسترسی متفاوتی باشند.
- داخل اکسسور set کلمه کلیدی value مقدار منتسب شده را در اختیار قرار میدهد تا در صورت لزوم بتوان بر روی آن پردازش لازم را انجام داد.
- یک پروپرتی میتواند فاقد اکسسور set باشد که در این صورت یک پروپرتی فقط خواندنی ایجاد میگردد. همچنین میتواند فقط شامل اکسسور set باشد که در این صورت فقط امکان انتساب مقادر به آن وجود دارد و امکان دریافت یا خواندن مقدار آن میسر نیست. چنین پروپرتی ای فقط نوشتنی خواهد بود.
- در بدنه اکسسور set الزامی به انتساب مقدار منتسب توسط کد مشتری نیست. در صورت صلاحدید میتوانید به جای آن هر مقدار دیگری را در نظر بگیرید یا عملیات مورد نظر خود را انجام دهید.
- در بدنه اکسسور get هم هر مقداری را میتوانید بازگشت دهید. یعنی الزامی وجود ندارد حتماً مقدار فیلد خصوصی متناظر با پروپرتی را بازگشت دهید. حتی الزامی به تعریف فیلد خصوصی برای هر پروپرتی ندارید. به طور مثال ممکن است مقدار بازگشتی اکسسور get حاصل محاسبه و ... باشد.
اکنون مثال دیگری را در نظر بگیرید. فرض کنید در یک پروژه فروشگاهی در حال تهیه کلاسی برای مدیریت محصولات هستید. قصد داریم یک پروپرتی ایجاد کنیم تا نام محصول را نگهداری کند و در حال حاضر هیچ محدودیتی برای نام یک محصول در نظر نداریم. کد زیر را ببینید.
public class Product { private string _name; public string Name { get { return _name; } set { _name = value; } } }
فرض کنید پس از مدتی متوجه شدید اگر نام بسیار طولانی ای برای محصول درج شود ظاهر برنامه شما دچار مشکل میشود. پس باید بر روی این مورد نظارت داشته باشید. دیدیم که برای رسیدن به این هدف باید فیلد عمومی را فراموش و به جای آن پروپرتی تعریف کنیم. اما مسئله اینست که تبدیل یک فیلد عمومی به پروپرتی میتواند سبب بروز ناسازگاریهایی در بخشهای دیگر برنامه که از این کلاس و آن فیلد استفاده میکنند شود. پس بهتر آن است که از ابتدا پروپرتی تعریف کنیم هر چند نیازی به عملیات نظارتی خاصی نداریم. در این حالت اگر نیاز به پردازش بیشتر پیدا شد به راحتی میتوانیم کد مورد نظر را در اکسسورهای موجود اضافه کنیم بدون آنکه نیازی به تغییر بخشهای دیگر باشد.
و یک خبر خوب! از سی شارپ ۳ به بعد ویژگی جدیدی در اختیار ما قرار گرفته است که میتوان پروپرتیهایی مانند مثال بالا را که نیازی به عملیات نظارتی ندارند، سادهتر و خواناتر تعریف نمود. این ویژگی جدید پروپرتی اتوماتیک یا Auto-Implemented Property نام دارد. مانند نمونه زیر.
public class Product { public string Name { get; set; } }
البته استفاده از پروپرتی برتری دیگری هم دارد. و آن کنترل سطح دسترسی اکسسورها است. مثال زیر را ببینید.
public class Student { public DateTime Birthdate { get; set; } public double Age { get; private set; } }
تذکر: در هنگام تعریف یک فیلد میتوان از کلمه کلیدی readonly استفاده کرد تا یک فیلد فقط خواندنی ایجاد گردد. اما در اینصورت فیلد تعریف شده حتی داخل کلاس هم فقط خواندنی است و فقط در هنگام تعریف یا در متد سازنده کلاس امکان مقدار دهی به آن وجود دارد. در بخشهای بعدی مفهوم سازنده کلاس مورد بررسی خواهد گرفت.
انتخاب عناصر صفحه
1-انتخاب عناصر صفحه برای ایجاد تغییر
خوشبختانه jQuery یک مکانیزم بسیار قوی و قدرتمند ارایه کرده است که انتخاب هر عنصری از صفحه را به سادگی امکان پذیر میسازد. انتخاب کنندههای jQuery از ساختار مربوط به CSS استفاده میکنند، بنابراین ممکن است شما هم اکنون با تعداد زیادی از آنها آشنا باشید . در ادامه شمار بیشتر و قدرتمندتری خواهید آموخت.
برای درک بهتر شما از مطالب مربوط به بخش انتخاب کننده ها، یک مثال آماده مختص به این مبحث، در قالب یک صفحه اینترنتی، را در فایل صفحه کارگاهی قرار داده ایم، این فایل در ادرس chapter2/lab.selector.htm قابل دسترسی میباشد. این مثال از پیش آماده و کامل (نوشته شده توسط نویسنده کتاب)، این امکان را به شما میدهد تا با وارد کردن یک رشته، به عنوان پارامتر انتخاب کننده، در همان زمان عنصر انتخاب کننده در صفحه را رویت کنید. زمانی که این صفحه را اجرا میکنید تصویری مانند زیر ظاهر خواهد شد.
برای درک بهتر مطالب این سلسله پستها میتوانید فایلهای کتاب را از آدرس اصلی آن یا از این آدرس در همین سایت دانلود نمایید.
این صفحه سه پنجره مجزا دارد. در پنجره سمت چپ ، یک textBox و یک دکمه دیده میشود، که با وارد کردن یک انتخاب کننده در textBox و فشردن دکمه، عنصر مورد نظر در پنجره سمت راست انتخاب میشود. برای شروع در textBox عبارت li را بنویسید و دکمه Apply را کلیک کنید.
با انجام این عمل تصویر زیر باید خروجی شما باشد. میتوانید حالتهای دیگر را خودتان امتحان کنید.
1-1- استفاده از انتخاب کنندههای ابتدایی CSS
برنامه نویسان وب برای اعمال فرمتهای ظاهری گوناگون به بخشها و عناصر مختلف یک صفحه اینترنتی، از ایک راه بسیار ساده، در عین حال قدرتمند و کارا استفاده میکنند که در تمام مرورگرهای مختلف نیز جوابگو باشد. این انتخاب کنندهها عناصر را بر اساس نام شناسه آنها، نام کلاس و یا ساختار سلسله مراتبی موجود در صفحه انتخاب میکنند.
در زیر به معرفی چند نمونه از این انتخاب کنندههای ساده CSS میپردازیم:
- a : تمام عناصر <a> را انتخاب میکند.
- specialID# : عنصری را که دارای ID با عنوان specialID باشد انتخاب میکند.
- specialClass. : عناصری را که دارای کلاس specialClass هستند انتخاب میکند.
- a#specialID.specialClass : این عبارت عنصری را انخاب میکند که شناسه آن specialID باشد، به شرط آنکه این عنصر <a> باشد و دارای کلاس specialClass نیز باشد را انتخاب میکند.
- p a.specialClass: تمام عناصر لینک (<a>) را که دارای کلاس specialClass باشند و درون یک عنصر پاراگراف (<p>) قرار گرفته باشند را انتخاب میکند.
این انتخاب کنندهها شاید ساده به نظر برسند، اما در بسیاری از مواقع پاسخگوی ما میباشند؛ به علاوه آنه که با ادغام این انتخاب کنندههای ساده، ما میتوانیم انتخاب کنندههای پیچیدهتر و تخصصیتر ایجاد کنیم.
نکته مثبت در مورد انتخاب کنندههای CSS این است که از همین انتخاب کنندهها میتوانیم در jQuery نیز استفاده کنیم. برای این کار تنها کافیست انتخاب کننده مورد نظر را به تابع ()$ ارسال کنیم. در زیر یک نمونه را مشاهده میکنید:
$("p a.specialClass")
2-1- استفاده از انتخاب کنندههای فرزند (Child) ، نگهدارنده (Container) و صفت (Attribute)
برای انتخاب کنندههای پیشرفته تر، jQuery از جدیدترین مرورگرهایی که CSS را پشتیبانی میکنند، استفاده میکند که میتوان به Mozilla Firefox, Internet Explorer 7, Safariو سایر مرورگرهای پیشرفته (مدرن) اشاره کرد. این انتخاب کنندههای پیشرفته شما را قادر میسازند تا مستقیما فرزند یک عنصر را انتخاب کنید و یا از ساختار سلسله مراتبی عناصر صفحه، مستقیما به عنصر مورد نظر دسترسی داشته باشید و یا حتی تمام عناصری که یک صفت خاص را شامل میشوند، انتخاب کنید. گاهی اوقات انتخاب فرزندی از یک شی برای ما مطلوب است. برای مثال ممکن است ما به چند مورد از یک لیست احتیاج داشته باشیم، نه یک زیر مجموعه ای از آن لیست. به قطعه کد زیر که از صفحه کارگاهی این پست گرفته شده است دقت نمایید:
<ul> <li><a href="http://jquery.com">jQuery supports</a> <ul> <li><a href="css1">CSS1</a></li> <li><a href="css2">CSS2</a></li> <li><a href="css3">CSS3</a></li> <li>Basic XPath</li> </ul> </li> <li>jQuery also supports <ul> <li>Custom selectors</li> <li>Form selectors</li> </ul> </li> </ul>
راه حل مناسب برای انتخاب چنین حالتی استفاده از انتخاب فرزند می باشد که به این منظور Parent (والد) و Child (فرزند)، به وسیله یک کاراکتر < از یکدیگر جدا میشوند:
p > a
ul.myList > li > a
انتخاب کنندههای صفت نیز بسیار قدرتمند میباشند و ما را تواناتر میسازند، فرض کنید برای منظوری خاص قصد دارید به تمام لینکهای موجود در صفحه که به مکانی خارج از این وب سایت اشاره دارند، رفتاری را اضافه کنید (مثلا مانند همین سایت به کنار آنها یک آیکن اضافه نمایید) . فرض کنید این کد (کد موجود در مثال کارگاهی) را در صفحه خود دارید:
<li><a href="http://jquery.com">jQuery supports</a> <ul> <li><a href="css1">CSS1</a></li> <li><a href="css2">CSS2</a></li> <li><a href="css3">CSS3</a></li> <li>Basic XPath</li> </ul> </li>
a[href^=http://]
می توانید این کد را در صفحه کار گاهی تست کنید.
راهای دیگری برای استفاده از انتخاب کنندههای صفت وجود دارد.
form[method]
input[type=text]
دستور زیر مثالی دیگر برای بررسی یک مقدار بر اساس کاراکترهای نخست آن میباشد:
div[title^=my]
اما اگر بخواهیم تنها بر اساس کاراکترهای انتهایی انتخابی انجام دهیم، دستور مناسب چه خواهد بود؟ برای چنین منظوری مانند زیر عمل میکنیم:
a[href$=.pdf]
a[href*=jquery.com]
فراتر از خصوصیات، بعضی مواقع ما میخواهیم بررسی کنیم که آیا یک عنصر شامل عنصر دیگری هست یا خیر. در مثالهای قبلی فرض کنید ما میخواهیم بدانیم که آیا یک li شامل a هست یا خیر، jQuery با استفاده از انتخاب کنندههای Containerها این را پشتیبانی میکند:
li:has(a)
تصویر زیر انتخاب گرهایی را نشان میدهد که ما میتوانیم در jQuery استفاده نماییم.
انشالله در پستهای بعدی ادامه مباحث را بررسی خواهد شد.