اگر یک دولوپر فرانتاِند هستید، زبان CSS یکی از مهمترین مواردی است که باید نحوۀ کار با آن را بیاموزید. خواه از کدهای CSS-in-JS (برنامهنویسی به زبان JS در مفسر CSS) برای توسعه استفاده کنید یا از کدهای سادهٔ CSS، آشنایی با مفاهیم پایهای این زبان برای نوشتن سورسکدی کارآمد و مقیاسپذیر یک ضرورت است. در این پست، به تشریح فیچری از زبان سیاساس تحت عنوان Cascade خواهیم پرداخت که ممکن است برخی دولوپرها درک درستی از آن نداشته باشند؛ همچنین چند روش را بررسی خواهیم کرد تا در صورت استفاده از این فیچر در طراحی صفحات وب، کنترل کد نوشته شده برایتان راحتتر و سادهتر گردد.
CSS مخفف کلمات Cascading Style Sheets است و همانطور که از نام این زبان مشخص است، آبشاری بودن به عنوان فیچری در ماهیت این زبان تعریف شده است. در واقع، در پروسهٔ طراحی رابط کاربری با زبان سیاساس میتوان قابلیت Cascade این زبان را به منزلهٔ ابزاری قدرتمند در نظر گرفت اما به خاطر داشته باشیم که استفادهٔ نادرست از این قابلیت میتواند به طراحی یکسری Style Sheet (الگو) ناکارآمد منجر شود که ایجاد تغییر در این دست الگوها میتواند به کابوسی برای دولوپرهای فرانتاِند تبدیل شود.
Cascade چه کاربردی دارد؟
این قابلیت زبان CSS فهرستی نامرتب از مقادیر تعریف شده برای یک پراپرتی از یک اِلِمان را گرفته، آنها را بر اساس اولویتی که برایشان تعریف شده مرتب میکند و یک به اصطلاح Value (مقدار) برای پراپرتی مد نظر را به صورت آبشاری (از اولویت اول تا اولویت آخر) در خروجی ارائه میدهد. به عبارت دیگر، Cascade الگوریتمی است که بواسطهٔ آن مرورگر تصمیم میگیرد تا کدام استایل سیاساس را برای اِعمال روی یک اِلِمان انتخاب کند (برخی دولوپرها استایل انتخابشده برای اِلِمان را استایل برنده نیز مینامند.) برای درک بهتر الگوریتم آبشاری زبان سیاساس، ابتدا میبایست با مفاهیمی همچون Selector، Property و Declaration آشنا شوید:
به طور کلی، الگوریتم آبشاری سیاساس اتربیوتها را گرفته و به هر یک از آنها وزنی اختصاص میدهد؛ بنابراین اگر دستوری در سطح اولویت بالاتر قرار بگیرد، برنده شده و روی اِلِمان مد نظر اِعمال خواهد شد. برای درک بهتر این موضوع، در ادامه لیستی از اتربیوتهایی را بیان میکنیم که الگوریتم آبشاری سیاساس آنها را بررسی میکند (این لیست از بیشترین ورزن به کمترین وزن مرتب شده است):
- منبع و اهمیت دستور (Origin & Importance)
- ارجحیت سلکتور (Selector Specificity)
- ترتیب نمایش (Order of Appearance)
- پراپرتیهای والد و پراپرتیهایی فرزند (Initial & Inherited Properties)
منبع و اهمیت دستور (Origin & Importance)
این اتربیوت، اولین اولویت را در الگوریتم آبشاری دارا است؛ به عبارت دیگر، Cascade در ابتدا آن را چک میکند که در واقع ترکیبی از اهمیت و منبع یک دستور خاص است. هر دستور سیاساس ممکن است از سه منبع مختلف ایجاد شود و اهمیت دستور به منبعی که از آن میآید بستگی دارد. منابع ممکن برای یک دستور سیاساس عبارتاند از:
- User-Agent: دستورات سیاساس با این منبع یکسری استایل پیشفرض برای اِلِمانها هستند که توسط مرورگر ارائه شدهاند و به همین دلیل نیز ممکن است یک وبسایت خاص در مرورگرهای مختلف کمی متفاوت به نظر برسد و به همین علت هم برخی از دولوپرها از استایلشیتهای به اصطلاح CSS Reset (ریست کردن استایل اِلِمانهای HTML) استفاده میکنند تا اطمینان حاصل شود که استایلهای پیشفرض مرورگر در آن اصطلاحاً Override (رونویسی) شدهاند.
- Override: اورراید (رونویسی) یک استایل اصطلاحی است بدین معنی که دستورات یک استایل در استایل دیگر رونویسی میشود یا یک استایل برخی پراپرتیهای استایل دیگر را به ارث میبرد و همچنین ممکن است در برخی پراپرتیها نیز تغییراتی اِعمال شود.
- User: این دست استایلها توسط کاربران مرورگرها تعریف و کنترل میشود. در واقع، تمام صفحات وب دارای این استایل نیستند اما برخی از کاربران آن را به مرورگر خود اضافه میکنند (معمولاً این کار به منظور تغییر استایلها به منظور شخصیسازی بیشتر مرورگر بسته به نیازهای شخصی کاربر صورت میگیرد.)
- Author: این دست استایلها همان کدهای CSS است که توسط دولوپرها و در داکیومنتهای HTML تعریف میشوند و به منزلهٔ تنها منبعی است که دولوپرهای فرانتاِند میتوانند آن را کدنویسی کرده و تحت کنترل کامل خود داشته باشند.
اهمیت یک دستور به منظور اِعمال تغییر در یک اِلِمان را میتوان توسط سینتکس !important در کد سیاساس تعریف کرد. با افزودن !important الگوریتم آبشاری سیاساس به طور خودکار اولولیت اول را به آن دستور میدهد. به طور کلی، استایلی که در آن دستور !important استفاده شده باشد را صرفاً میتوان با دستوراتی به اصطلاح Override کرد که آنها نیز از دستور !important برخوردار باشند که با گذر زمان و توسعهٔ پروژه، این موضوع میتواند کد سیاساس را بسیار آسیبپذیر سازد! در همین راستا، بسیاری از دولوپرهای حرفهای فرانتاند توصیه میکنند فقط زمانی از دستور !important استفاده کنید که برای اولویتبندی دستورات هیچ راهی جز استفاده از این سینتکس را ندارید (مثلاً کار با استایلهای به اصطلاح Third Party).
الگوریتم آبشاری سیاساس (CSS Cascade) به منظور مشخص کردن به اصطلاح Declaration برندهای که قرار هست روی اِلِمان مد نظر اعمال شود، ترکیبی از دو اتربیوت را در نظر گرفته و این در حالی است که هر ترکیبی دارای وزنی میباشد (مشابه قسمتهایی از تعریف سیاساس که برای آنها وزن در نظر گرفتیم) و دستور با بالاترین وزن برنده خواهد شد. همچنین مرورگرها میتوانند برای آیتم Origin & Importance ترکیبهای مختلفی را در نظر بگیرند که در ادامه این ترکیبها از بیشترین تا کمترین وزن لیست شدهاند:
- !User-Agent & important
- !User & important
- !Author &!important
- CSS Animations & @keyframes
- (Author (Normal Weight
- (User (Normal Weight
- (User Agent (Normal Weight
هنگامی که مرورگر برای تصمیمگیری در مورد انتخاب از میان دو یا چند دستور سیاساس به منظور اِعمال تغییر در یک اِلِمان دچار مشکل میشود و یکی از دستورات با در نظر گرفتن معیار Origin & Importance در اولویت سطح اول قرار میگیرد، الگوریتم آبشاری این زبان آن استایل را در نظر خواهد گرفت. با این حال، اگر دستورات برای اِعمال روی یک اِلِمان با در نظر گرفتن معیار Origin & Importance اولویت یکسانی داشته باشند، الگوریتم آبشاری به سراغ سنجش معیار Selector Specificity (ارجحیت سلکتور) خواهد رفت.
ارجحیت سلکتورها (Selector Specificity)
معیار دوم در الگوریتم آبشاری این زبان اصطلاحاً Selector Specificity نام دارد که در این سطح، مرورگر سلکتورهای به کار رفته در دستور سیاساس را در نظر میگیرد. همانطور که پیش از این اشاره شد، دولوپرهای فرانتاِند تنها روی استایلهایی با منبع Author (دولوپر سایت) کنترل دارند؛ به عبارت دیگر، ایشان نمیتوانند برای تغییر منبع یک دستور کار زیادی انجام دهند! با این حال، اگر در کدنویسی از دستور !important کمتر استفاده کنند، میتوانند در سطح Specificity کنترل زیادی بر الگوریتم آبشاری داشته باشند.
به همان شیوهای که به معیار ترکیبیِ Origin & Importance وزن داده میشود، انواع مختلف سلکتورهای سیاساس نیز اولویتبندی میشوند. هنگام ارزیابی معیار Selector Specificity، تعداد سلکتورها و اولویت آنها در نظر گرفته شده و از همین روی سلکتورهای سیاساس میتوانند به یکی از سطوح وزنی زیر تعلق داشته باشند:
- Inline Styles (هر آنچه داخل تگ <style> باشد.)
- ID Selectors
- Classes / Sseudo-Selectors
- Type Selectors (برای مثال سلکتور نوع <h1>) و Pseudo-Elements
دو دستور برای اِعمال تغییر روی یک اِلِمان سیاساس را در نظر بگیرید؛ در این دو دستور، تعداد سلکتورهایی که اولویت بالایی دارند یکسان بوده و از همین روی این الگوریتم پس از در نظر گرفتن Specificity (ارجحیت سلکتورها)، اقدام به بررسی تعداد سلکتورها میکند. برای مثال، در کد زیر که هر دو دستور سیاساس یک اِلِمان را هدف قرار دادند، رنگ اِلِمان مد نظر قرمز میشود چرا که هر دو دستور دارای یک شناسۀ سلکتور h1 هستند، اما دستور دوم به تعداد دو سلکتور از نوع class را دارا است:
#first .blue h1 {
color: blue;
}
#second .red.bold h1 {
color: red;
}
بسیاری از دولوپرها Selector Specificity را با عدم تکیه بر آنها مدیریت میکنند. در واقع، پایین نگاه داشتن اولویت معیار Specificity باعث میشود که دستورات سیاساس دولوپر انعطافپذیر باقی بمانند. همچنین اگر دولوپری به طور پیشفرض از یکسری کلاس برای استایلهای سفارشی خود، به راحتی میتواند استایلها را در هنگام نیاز اصطلاحاً Override کند اما این در حالی است که اگر در دستورات سیاساس میزان Selector Specificity خیلی بالا باشد، این بدان معنا است که گویی از دستور !important داخل کد خود استفاده شده و میتواند خیلی زود کد را دچار مشکل کند.
ترتیب نمایش (Order of Appearance)
آخرین معیار اصلی الگوریتم آبشاری زبان سیاساس، اِعمال تغییر در اِلِمانها بر اساس ترتیب دستورات است. هنگامی که دو سلکتور دارای ارجحیت یکسان باشند، دستوری که در آخر سورسکد میآید، به عنوان پراپرتی برنده روی اِلِمان مد نظر اعمال خواهد شد؛ بنابراین ترتیب دستورات در استایلشیت بسیار مهم است.
برای مثال، اگر شما دو استایلشیت در قسمت هِد داکیومنت HTML خود داشته باشید، فایل دوم دستورات نوشته شده در فایل اول را به اصطلاح Override میکند و به همین دلیل است که اگر دولوپری از CSS Reset (ریست کردن استایل اِلِمانهای HTML) یا یک فریمورک سیاساس مثل Bootstrap استفاده کند، بایستی آن را قبل از استایلهای سفارشی خود لود کند.
پراپرتیهای والد و پراپرتیهایی فرزند (Initial & Inherited Properties)
در حالی که این دست دستورات سیاساس و مقادیرشان به عنوان معیار سنجش در الگوریتم آبشاری محسوب نمیشوند، اما با استفاده از آنها میتوان تعیین کرد در صورتی که هیچ دستوری از سیاساس برای اِعمال تغییر روی اِلِمانها وجود نداشته باشد، این مقادیر به عنوان مقادیر پیش فرض برای یک پراپرتی در نظر گرفته شده و روی اِلِمان مد نظر اِعمال شوند.
در واقع، این قسمت از الگوریتم CSS Cascade همان چیزی است که اکثر کاربران و دولوپرها با دیدن کلمۀ Cascade (آبشار) به آن فکر میکنند چرا که استایلها از والد به فرزند میرسند. به عبارت دیگر، پراپرتیهایی که از پراپرتی والد ارثبری داشتهاند، از اِلِمان والد به اِلِمان فرزند خواهند رسید. به عنوان مثال، تگ <p> با یک فونت به اصطلاح Monospace و متن قرمز رِندِر میشود چرا که اِلِمان والدش از چنین استایلهایی برخوردار است:
<div style="font-family: monospace; color: red;">
<p>inheritance can be super useful!</p>
</div>
برای پراپرتیهایی که چیزی را به ارث نبردهاند، هر اِلِمان دارای مجموعهای از مقادیر اولیه است که این مقادیر در هستهٔ سیاساس برای هر دستور تعریف شده است. به عنوان مثال، مقدار اولیه برای پراپرتی background-color رنگ transparent است و اگر در فایل استایلشیت خود هیچ مقداری برای background-color یک اِلِمان تعریف نشود، مقدار آن به طور پیشفرض transparent (شفاف) در نظر گرفته خواهد شد. علاوه بر این، دولوپرها میتوانند به صورت کاملاً صریح مقادیر پراپرتیهایی که از پراپرتی والد ارثبری داشتهاند یا مقدار پراپرتیهای اصلی را با استفاده از واژگان کلیدی inherit و initial در دستورات سیاساس خود استفاده کنند. به عنوان نمونه داریم:
div {
background-color: initial;
color: inherit;
}
با دانستن این نکات چگونه میتوان به دولوپر فرانتاند بهتر مبدل شد؟
از آنجایی که الگوریتم Cascade (آبشاری) بخشی از زبان CSS است که درک آن کمی مشکل بوده و اغلب منشاء بسیاری از باگها است، آشنایی با نحوۀ کار این الگوریتم موجب میشود تا دولوپرها بتوانند الگوهای خود را به شکلی مدیریت کنند که قابلنگاهداری بوده و به این درک برسند که چگونه میتوان با استفاده از معیار Selector Specificity این زبان یک کد خوب نوشت.
در بسیاری از موارد، دولوپرها در کدهای سیاساس خود از دستور !important برای اولویتبندی راحتتر و سریعتر به یک اِلِمان استفاده میکنند و این در حالی است که با استفاده از سلکتورهای با ارجحیت بالا، به خوبی میتوان این کار را انجام داد به طوری که اگر دولوپرها در درجه اول از کلاسها به عنوان سلکتور استفاده کنند، به راحتی میتوانند این کار را با تودرتو کردن سلکتورها انجام دهند یا زمانی که نیاز به اورراید کردن یک استایل از استایل دیگر دارند، میتوانند این کار را با افزودن یک سلکتور نوع class انجام دهند. روی هم رفته، آشنایی بهتر با الگوریتم Cascade موجب میشود تا دولوپرها توانایی کنترل بیشتری روی کد خود داشته باشند و امکان مدیریت استایلها برای ایشان نیز سادهتر خواهد شد.