نگهداری و گسترش پروژه های بزرگ یکی از مهمترین مراحل توسعه است که در تمام زبان ها و تکنولوژی ها اهمیت خود را نشان می دهد. در مورد طراحی صفحات وب و سی اس اس هم این موضوع وجود دارد و از اهمیت بالایی برخوردار است. یکی از مباحثی که در این رابطه همیشه مطرح می شود نامگذاری کلاس ها می باشد که روش های زیادی به مرور معرفی شدند که در بین این روش ها روش BEM محبوبیت خاصی بین توسعه دهندگان دارد.
متدهای مختلفی در زمینه css وجود داره از جمله ،BEM ،OOCSS، SMACSS، SUITCSS،Atomic
که در اینجا به بررسی روش BEM می پردازیم
مقدمه
متد BEM که مخفف سرواژگان Element ،Block و Modifier میباشد، روشی کامپوننت محور است در زمینهی توسعهی وب.
ایدهی اصلی این متد، تقسیم رابط کاربری (User Interface) به بلاکهای مستقل است. این شیوه، عملیات توسعه را آسان و سریع کرده و همچنین امکان استفادهی مجدد کدها را فراهم میکند.
بلاک (Block)
بلاک: جزء (component) مستقلی از صفحه که قابلیت استفاده مجدد را داراست.
در سند HTML، بلاکها در ویژگی class نمایش داده میشوند.
نام بلاک (Block) باید هدف آن را توصیف کند. در واقع چیستی آن را، مثلا menu. نه این که وضعیت ظاهری و شکل آن را بیان کند، مثلا نباید چیزی شبیه red یا big باشد.
مثال: <div class="error"></div> <div class="red-text"></div>
در مثال بالا حالت اول صحیح است چرا که از لحاظ معنایی، error وضعیت و حالت را بیان میکند در حالی که red-text بیانکننده ظاهر و استایل است.
یک Block نباید محیط اطرافش را تحت تأثیر قرار دهد. یعنی نباید margin یا position به خصوصی داشته باشد.
همچنین وقتی که از روش BEM استفاده میکنید، نباید از تگها و ID عناصر در CSS استفاده کنید، بلکه فقط استفاده از کلاس مجاز است.
این موارد، استقلال مورد نیاز Block را برای استفادهی مجدد یا جابجایی آن به هر قسمتی از پروژه، تضمین میکند.
ویژگیها:
۱) تورفتگی (Nesting)
بلاکها را میتوان داخل یکدیگر استفاده کرد. همچنین عمق این تورفتگیها به هر تعدادی میتواند باشد.
برای مثال بلاک head میتواند شامل فهرست (menu)، لوگو (logo)، فرم جستجو (search) و بخش ورود (auth) باشد.
<!-- `header` block --> <header class="header"> <!-- Nested `logo` block --> <div class="logo"></div> <!-- Nested `search-form` block --> <form class="search-form"></form> </header>
در مثال بالا دو بلاک search-form
و logo
، داخل بلاک header
قرار گرفتهاند.
بلاکها به علت ماهیت مستقلشان میتوانند بدون نیاز به تغییر در کدهای CSS یا جاوااسکریپتی که دارند، به هر نقطهای از صفحه جابجا شوند.
برای مثال بلاک logo
به راحتی میتواند با بلاک auth
جابجا شود، بدون نیاز به تغییر در کدهای CSS یا جاوااسکرپیت.
بلاکها میتوانند به هر تعداد مورد استفادهی مجدد قرار بگیرند. در واقع ممکن است در یک طرح چند نمونه از یک بلاک استفاده شود.
عنصر: قسمتی از یک بلاک (block) که به صورت مجزا قابل استفاده نیست.
برای مثال آیتمهای یک منو خارج از فضای اصلی آن استفاده نمیشوند بنابراین هر آیتم یک عنصر است.
نام عنصر (Element) باید هدف آن را توصیف کند. در واقع چیستی آن را، مثلا item
یا text
. نه این که بیانگر شکل ظاهری آن باشد، مثلا نباید چیزی شبیه red
یا big
باشد.
ساختار نام کامل یک عنصر به صورت block-name__element-name
است.
نام عنصر توسط دو تا underscore ( __
) از نام بلاک جدا میشود.
<!-- `search-form` block --> <form class="search-form"> <!-- `input` element in the `search-form` block --> <input class="search-form__input"> <!-- `button` element in the `search-form` block --> <button class="search-form__button">Search</button> </form>
در مثال بالا search-form
بلاک است و search-form__input
و search-form__button
دو عنصر متعلق به آن هستند.
عنصرها را نیز همانند بلاکها میتوان داخل یکدیگر استفاده کرد. همچنین عمق این تورفتگیها به هر تعدادی میتواند باشد.
یک عنصر همیشه بخشی از یک بلاک است نه یک عنصر دیگر.
به این معنا که نام عناصر نمیتواند به صورت سلسله مراتبی مثل block__elem1__elem2
تعریف شود.
<!-- Correct. The structure of the full element name follows the pattern: `block-name__element-name` --> <form class="search-form"> <div class="search-form__content"> <input class="search-form__input"> <button class="search-form__button">Search</button> </div> </form> <!-- Incorrect. The structure of the full element name doesn't follow the pattern: `block-name__element-name` --> <form class="search-form"> <div class="search-form__content"> <!-- Recommended: `search-form__input` or `search-form__content-input` --> <input class="search-form__content__input"> <!-- Recommended: `search-form__button` or `search-form__content-button` --> <button class="search-form__content__button">Search</button> </div> </form>
در حالت دوم مثال بالا، نامگذاری عناصر طبق الگو صورت نگرفته و صحیح نیست.
در واقع نام بلاک، namespace
را تعریف میکند، که نشان میدهد عناصر به کدام بلاک وابسته هستند: block__elem
.
یک بلاک میتواند ساختار تودرتویی از عناصر را در DOM داشته باشد.
<div class="block"> <div class="block__elem1"> <div class="block__elem2"> <div class="block__elem3"></div> </div> </div> </div>
با این حال در متدولوژی BEM، این ساختار بلاک همیشه نمایندهی لیستی مسطح از عناصر است.
.block {} .block__elem1 {} .block__elem2 {} .block__elem3 {}
که این امکان به وجود میآید تا بتوان بدون نیاز به تغییر دادن کدهای هر عنصر، ساختار DOM بلاک را تغییر داد.
<div class="block"> <div class="block__elem1"> <div class="block__elem2"></div> </div> <div class="block__elem3"></div> </div>
در مثال بالا گر چه ساختار بلاک تغییر کرده اما قواعد عناصر و نامگذاری آنها همچنان باقی و حکمفرماست.
یک عنصر همیشه عضو یک بلاک است و نباید به صورت مجزا از بلاک، مورد استفاده قرار بگیرد.
<!-- Correct. Elements are located inside the `search-form` block --> <!-- `search-form` block --> <form class="search-form"> <!-- `input` element in the `search-form` block --> <input class="search-form__input"> <!-- `button` element in the `search-form` block --> <button class="search-form__button">Search</button> </form> <!-- Incorrect. Elements are located outside of the context of the `search-form` block --> <!-- `search-form` block --> <form class="search-form"> </form> <!-- `input` element in the `search-form` block --> <input class="search-form__input"> <!-- `button` element in the `search-form` block--> <button class="search-form__button">Search</button>
در حالت دوم مثال بالا، دو عنصر search-form__input
و search-form__button
که متعلق به بلاک search-form
هستند، خارج از فضای بلاک خود استفاده شدهاند که غلط است !
یک عنصر جزء اختیاری بلا ک است. همهی بلاکها، لزوما شامل عنصر نیستند.
<!-- `search-form` block --> <div class="search-form"> <!-- `input` block --> <input class="input"> <!-- `button` block --> <button class="button">Search</button> </div>
فرضا در مثال بالا input
و button
عنصرهای بلاک search-form
نیستند و هر کدام برای خودشان بلاکی شدند ماشالا !
اگر قرار است ـ یا باید این امکان باشد ـ که این بخش مجددا در صفحه مورد استفاده قرار بگیرد و همچنین اگر این بخش به اجزا و بخشهای دیگر صفحه وابستگی ندارد، باید بلاک بسازیم.
اما اگر امکان استفادهی مجدد آن به صورت مجزا (بدون وابستگی به به بخشی دیگر) وجود نخواهد داشت، یعنی اگر بخواهیم این بخش را مجدد استفاده کنیم نیاز داریم بخشهای دیگری را هم بیاوریم آن وقت باید. عنصر بسازیم.
پیراینده: ماهیتی است که ظاهر، حالت و رفتار یک بلاک یا عنصر را مشخص میکند.
برای مثال ظاهر یک بلاک فهرست (menu
) میتواند توسط یک پیراینده تغییر کند.
نام پیراینده میتواند بیانگر هر یک از موارد زیر باشد:
size_s
)disabled
یا focused
)directions_left-top
)نام پیراینده توسط یک underscore ( _
) از بلاک یا عنصر جدا میشود.
این نوع پیراینده زمانی استفاده میشود که صرفا بودن یا نبودن پیراینده مورد اهمیت است نه مقدار آن. برای مثال disabled
.
وقتی یک Modifier از نوع Boolean استفاده میشود در واقع بیانگر True بودن مقدار آن است.
ساختار نام کامل این نوع پیراینده به صورت زیر است:
block-name_modifier-name
(پیرایندهی یک بلاک)
block-name__element-name_modifier-name
(پیرایندهی یک عنصر)
<!-- The `search-form` block has the `focused` Boolean modifier --> <form class="search-form search-form_focused"> <input class="search-form__input"> <!-- The `button` element has the `disabled` Boolean modifier --> <button class="search-form__button search-form__button_disabled">Search</button> </form>
در مثال بالا search-form_focused
پیرایندهی بلاک search-form
و search-form__button_disabled
پیرایندهی عنصر search-form__button
است و هر دو پیراینده از نوع Boolean میباشند.
این نوع پیراینده زمانی استفاده میشود که مقدار پیراینده اهمیت دارد. برای مثال برای منویی با تم islands
داریم: menu_theme_islands
.
ساختار نام کامل این نوع پیراینده به صورت زیر است:
block-name_modifier-name_modifier-value
(پیرایندهی یک بلاک)
block-name__element-name_modifier-name_modifier-value
(پیرایندهی یک عنصر)
<!-- The `search-form` block has the `theme` modifier with the value `islands` --> <form class="search-form search-form_theme_islands"> <input class="search-form__input"> <!-- The `button` element has the `size` modifier with the value `m` --> <button class="search-form__button search-form__button_size_m">Search</button> </form> <!-- You can't use two identical modifiers with different values simultaneously --> <form class="search-form search-form_theme_islands search-form_theme_lite"> <input class="search-form__input"> <button class="search-form__button search-form__button_size_s search-form__button_size_m"> Search </button> </form>
در قسمت اول مثال بالا search-form_theme_islands
پیرایندهی بلاک search-form
و search-form__button_size_m
پیرایندهی عنصر search-form__button
است و هر دو پیراینده از نوع Key-value میباشند.
در قسمت دوم مثال، دو پیراینده که Key یکسان و Value متفاوت دارند، به صورت همزمان برای یک بلاک یا عنصر مورد استفاده قرار گرفتند که از لحاظ منطقی صحیح نیست.
یک پیراینده هیچ وقت به تنهایی استفاده نمیشود.
از دیدگاه BEM، یک پیراینده را نمیتواند فارغ از یک بلاک یا عنصر استفاده کرد. بلکه یک پیراینده باید ظاهر، حالت یا رفتار یک ماهیتی را (بلاک یا عنصر) تغییر دهد.
<!-- Correct. The `search-form` block has the `theme` modifier with the value `islands` --> <form class="search-form search-form_theme_islands"> <input class="search-form__input"> <button class="search-form__button">Search</button> </form> <!-- Incorrect. The modified class `search-form` is missing --> <form class="search-form_theme_islands"> <input class="search-form__input"> <button class="search-form__button">Search</button> </form>
در قسمت دوم مثال بالا، پیرایندهی search-form_theme_islands
که مربوط به بلاک search-form
است به صورت تنها استفاده شده که صحیح نیست.
قواعدی که تا اینجا برای نامگذاری ذکر شد باید در چارچوب اعداد و حروف کوچک انگلیسی استفاده شوند، همچنین برای جدا کردن کلمات از یکدیگر باید از خط تیره ( -
) استفاده کرد.
اما چندین روش جایگزین نیز در نامگذاری وجود دارد که میتوان مورد استفاده قرار داد.
block-name__elem-name--mod-name
-
) از هم جدا میشوند._
) از نام بلاک جدا میشود.--
) از نام عنصر یا بلاک مربوط جدا میشوند.تذکر: گاهی ممکن است دو خط تیرهی استفاده شده، در اعتبار سنجی سند HTML به عنوان comment تفسیر شوند.
MyBlock__SomeElem_modName_modVal
این روش بر پایهی شیوهی معروف camelCase است با این تفاوت که نام ماهیتهای BEM (بلاک، عنصر یا پیراینده) توسط underscore از یکدیگر جدا میشوند.
blockName-elemName--modName--modVal
-
) از نام بلاک جدا میشود.--
) از نام عنصر یا بلاک مربوط جدا میشود.--
) از نام آن جدا میشود.تذکر: گاهی ممکن است دو خط تیرهی استفاده شده، در اعتبار سنجی سند HTML به عنوان comment تفسیر شوند.
در متدولوژی BEM، قواعد کلی نامگذاری مطرح میشود و انتخاب روش نامگذاری کاملا به نیازمندیهای شما و پروژه بستگی دارد. در واقع روشی بهتر است که بهتر پاسخگوی نیازهای شما باشد.
وقتی از BEM استفاده کنید تمام انتخابگرهای CSS به اصطلاح flat خواهند بود. به این معنی که انتخابگرهای نسلی مثل انتخابگر زیر که بار محاساباتی سنگینتری دارند، وجود نخواهند داشت. در نتیجه سرعت بارگذاری صفحه به مراتب بالاتر خواهد رفت.
.header .nav .nav-item {}
برای بررسی بیشتر این موضوع به مطلب مربوط به بهبود کارایی انتخابگرها مراجعه کنید.
تمام اطلاعات مورد نیاز با بررسی کد HTML به دست خواهد آمد. به سرعت پیرایندهها و عناصر متعلق به یک بلاک معلوم میشوند. میتوان فورا به کدهای CSS مربوطه دست پیدا کرد، بدون نیاز به چک کردن وابستگیهای پشت سر هم.
از آنجایی که هر بلاک کاملا مستقل است، میتوان بدون هیچ گونه نگرانی آن را به هر نقطهای از صفحه انتقال داد یا کپی کرد. همچنین چون در BEM فقط از کلاس استفاده میشود هر زمان که نیاز شد میتوان تگها را تغییر داد.
علاوهی بر اینها، BEM به راحتی قابل فهم و یادگیری آن بسیار آسان است.
مهمترین ایرادی که در BEM به وجود میآید طولانی و زشت شدن نام کلاسهاست که خب برای آنها که زیبایی کد را به قابلیت نگهداری آن ترجیح میدهند مهم است !
علاوه بر BEM، متدهای دیگری نیز در زمینه CSS وجود دارند مثل OOCSS، SMACSS، SUITCSS و Atomic که اگر علاقهمند هستید میتوانید دربارهی آنها بیشتر بخوانید.