آموزش روش BEM برای نامگذاری کلاس ها در طراحی صفحات وب

معرفی روش BEM برای نامگذاری کلاس‎ها

نگهداری و گسترش پروژه های بزرگ یکی از مهمترین مراحل توسعه است که در تمام زبان ها و تکنولوژی ها اهمیت خود را نشان می دهد. در مورد طراحی صفحات وب و سی اس اس هم این موضوع وجود دارد و از اهمیت بالایی برخوردار است. یکی از مباحثی که در این رابطه همیشه مطرح می شود نام‌گذاری کلاس ها می باشد که روش های زیادی به مرور معرفی شدند که در بین این روش ها روش BEM محبوبیت خاصی بین توسعه دهندگان دارد. در این مطلب حسن علیزاده به عنوان نویسنده مهمان این روش را معرفی می کنند.

مقدمه

متد BEM که مخفف سرواژگان Element ،Block و Modifier می‌باشد، روشی کامپوننت محور است در زمینه‌ی توسعه‌ی وب.

ایده‌ی اصلی این متد، تقسیم رابط کاربری (User Interface) به بلاک‌های مستقل است. این شیوه، عملیات توسعه را آسان و سریع کرده و همچنین امکان استفاده‌ی مجدد کدها را فراهم می‌کند.

بلاک (Block)

بلاک: جزء (component) مستقلی از صفحه که قابلیت استفاده مجدد را داراست.

در سند HTML، بلاک‌ها در ویژگی class نمایش داده می‌شوند.

نام بلاک (Block) باید هدف آن را توصیف کند. در واقع چیستی آن را، مثلا menu. نه این که وضعیت ظاهری و شکل آن را بیان کند، مثلا نباید چیزی شبیه red یا big باشد.

مثال:
<!-- Correct. The `error` block is semantically meaningful -->
<div class="error"></div>

<!-- Incorrect. It describes the appearance -->
<div class="red-text"></div>

در مثال بالا حالت اول صحیح است چرا که از لحاظ معنایی، error وضعیت و حالت را بیان می‌کند در حالی که red-text بیان‌کننده ظاهر و استایل است.

 

یک Block نباید محیط اطرافش را تحت تأثیر قرار دهد. یعنی نباید margin یا position به خصوصی داشته باشد.

همچنین وقتی که از روش BEM استفاده می‌کنید، نباید از تگ‌ها و ID عناصر در CSS استفاده کنید، بلکه فقط استفاده از کلاس مجاز است.

این موارد، استقلال مورد نیاز Block را برای استفاده‌ی مجدد یا جابجایی آن به هر قسمتی از پروژه، تضمین می‌کند.

ویژگی‌ها:

۱) تورفتگی (Nesting)

بلاک‌ها را می‌توان داخل یکدیگر استفاده کرد. همچنین عمق این تورفتگی‌ها به هر تعدادی می‌تواند باشد.

برای مثال بلاک head می‌تواند شامل فهرست (menu)، لوگو (logo)، فرم جستجو (search) و بخش ورود (auth) باشد.

 
bem block nested structure
 

مثال:

<!-- `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 قرار گرفته‌اند.

۲) جاگیری دلخواه (Arbitrary placement)

بلاک‌ها به علت ماهیت مستقلشان می‌توانند بدون نیاز به تغییر در کدهای CSS یا جاوااسکریپتی که دارند، به هر نقطه‌ای از صفحه جابجا شوند.

برای مثال بلاک logo به راحتی می‌تواند با بلاک auth جابجا شود، بدون نیاز به تغییر در کدهای CSS یا جاوااسکرپیت.

 
bem arbitrary placement
 

 
bem arbitrary placement
 

۳) استفاده‌ی مجدد (Re-use)

بلاک‌ها می‌توانند به هر تعداد مورد استفاده‌ی مجدد قرار بگیرند. در واقع ممکن است در یک طرح چند نمونه از یک بلاک استفاده شود.

 
bem block reuse
 

عنصر (Element)

عنصر: قسمتی از یک بلاک (block) که به صورت مجزا قابل استفاده نیست.

برای مثال آیتم‌های یک منو خارج از فضای اصلی آن استفاده نمی‌شوند بنابراین هر آیتم یک عنصر است.

 
bem element
 

نام عنصر (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 نیستند و هر کدام برای خودشان بلاکی شدند ماشالا !

الان باید بلاک بسازم یا عنصر ؟!

اگر قرار است ـ یا باید این امکان باشد ـ که این بخش مجددا در صفحه مورد استفاده قرار بگیرد و همچنین اگر این بخش به اجزا و بخش‌های دیگر صفحه وابستگی ندارد، باید بلاک بسازیم.

اما اگر امکان استفاده‌ی مجدد آن به صورت مجزا (بدون وابستگی به به بخشی دیگر) وجود نخواهد داشت، یعنی اگر بخواهیم این بخش را مجدد استفاده کنیم نیاز داریم بخش‌های دیگری را هم بیاوریم آن وقت باید. عنصر بسازیم.

پیراینده (Modifier)

پیراینده: ماهیتی است که ظاهر، حالت و رفتار یک بلاک یا عنصر را مشخص می‌کند.

برای مثال ظاهر یک بلاک فهرست (menu) می‌تواند توسط یک پیراینده تغییر کند.

 
bem modifier
 

نام پیراینده می‌تواند بیانگر هر یک از موارد زیر باشد:

  • شکل ظاهری آن، مثلا این که چه اندازه‌ای دارد (size_s)
  • حالت آن، مثلا این که چه تفاوتی دارد (disabled یا focused)
  • رفتار آن، مثلا این که چه واکنشی نشان می‌دهد (directions_left-top)

نام پیراینده توسط یک underscore ( _ ) از بلاک یا عنصر جدا می‌شود.

انواع پیراینده (Modifier)

۱) نوع Boolean

این نوع پیراینده زمانی استفاده می‌شود که صرفا بودن یا نبودن پیراینده مورد اهمیت است نه مقدار آن. برای مثال 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 می‌باشند.

۲) نوع Key-value

این نوع پیراینده زمانی استفاده می‌شود که مقدار پیراینده اهمیت دارد. برای مثال برای منویی با تم 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 متفاوت دارند، به صورت همزمان برای یک بلاک یا عنصر مورد استفاده قرار گرفتند که از لحاظ منطقی صحیح نیست.

استفاده از پیراینده (Modifier)

یک پیراینده هیچ وقت به تنهایی استفاده نمی‌شود.

از دیدگاه 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 است به صورت تنها استفاده شده که صحیح نیست.

روش‌های جایگزین در نام‌گذاری

قواعدی که تا اینجا برای نام‌گذاری ذکر شد باید در چارچوب اعداد و حروف کوچک انگلیسی استفاده شوند، همچنین برای جدا کردن کلمات از یکدیگر باید از خط تیره ( - ) استفاده کرد.

اما چندین روش جایگزین نیز در نام‌گذاری وجود دارد که می‌توان مورد استفاده قرار داد.

۱) Two Dashes style

block-name__elem-name--mod-name

  • نام‌ها با حروف کوچک انگلیسی (lower case) نوشته می‌شوند.
  • کلمات با خط تیره ( - ) از هم جدا می‌شوند.
  • نام عنصر توسط یک underscore ( _ ) از نام بلاک جدا می‌شود.
  • نام پیراینده‌های از نوع Boolean توسط دو تا خط تیره ( -- ) از نام عنصر یا بلاک مربوط جدا می‌شوند.

تذکر: گاهی ممکن است دو خط تیره‌ی استفاده شده، در اعتبار سنجی سند HTML به عنوان comment تفسیر شوند.

۲) CamelCase style

MyBlock__SomeElem_modName_modVal

این روش بر پایه‌ی شیوه‌ی معروف camelCase است با این تفاوت که نام ماهیت‌های BEM (بلاک، عنصر یا پیراینده)‌ توسط underscore از یکدیگر جدا می‌شوند.

۳) “Sans underscore” style

blockName-elemName--modName--modVal

  • نام‌ها به صورت camelCase نوشته می‌شوند.
  • نام عنصر توسط یک خط تیره ( - ) از نام بلاک جدا می‌شود.
  • نام پیراینده توسط دو تا خط تیره ( -- ) از نام عنصر یا بلاک مربوط جدا می‌شود.
  • مقدار یک پیراینده توسط دو تا خط تیره ( -- ) از نام آن جدا می‌شود.

تذکر: گاهی ممکن است دو خط تیره‌ی استفاده شده، در اعتبار سنجی سند HTML به عنوان comment تفسیر شوند.

کدام روش نام‌گذاری بهتر است ؟

در متدولوژی BEM، قواعد کلی نام‌گذاری مطرح می‌شود و انتخاب روش نام‌گذاری کاملا به نیازمندی‌های شما و پروژه بستگی دارد. در واقع روشی بهتر است که بهتر پاسخگوی نیازهای شما باشد.

مزایای BEM

کارایی (Performance)

وقتی از BEM استفاده کنید تمام انتخابگرهای CSS به اصطلاح flat خواهند بود. به این معنی که انتخابگرهای نسلی مثل انتخابگر زیر که بار محاساباتی سنگین‌تری دارند، وجود نخواهند داشت. در نتیجه سرعت بارگذاری صفحه به مراتب بالاتر خواهد رفت.


.header .nav .nav-item {}

برای بررسی بیشتر این موضوع به مطلب مربوط به بهبود کارایی انتخابگرها مراجعه کنید.

خوانایی (Readability)

تمام اطلاعات مورد نیاز با بررسی کد HTML به دست خواهد آمد. به سرعت پیراینده‌ها و عناصر متعلق به یک بلاک معلوم می‌شوند. می‌توان فورا به کدهای CSS مربوطه دست پیدا کرد، بدون نیاز به چک کردن وابستگی‌های پشت سر هم.

انعطاف‌پذیری (Flexibility)

از آنجایی که هر بلاک کاملا مستقل است، می‌توان بدون هیچ گونه نگرانی آن را به هر نقطه‌ای از صفحه انتقال داد یا کپی کرد. همچنین چون در BEM فقط از کلاس استفاده می‌شود هر زمان که نیاز شد می‌توان تگ‌ها را تغییر داد.

علاوه‌ی بر این‌ها، BEM به راحتی قابل فهم و یادگیری آن بسیار آسان است.

ایراد ‌BEM

مهم‌ترین ایرادی که در BEM به وجود می‌آید طولانی و زشت شدن نام کلاس‌هاست که خب برای آنها که زیبایی کد را به قابلیت نگه‌داری آن ترجیح می‌دهند مهم است !

اصلا به قول آقای Nicolas Gallagher:

Replace “can you build this?” with “can you maintain this without losing your minds?”

روش‌های جایگزین

علاوه بر BEM، متد‌های دیگری نیز در زمینه CSS وجود دارند مثل OOCSS، SMACSS، SUITCSS و Atomic که اگر علاقه‌مند هستید می‌توانید درباره‌ی آنها بیشتر بخوانید.

نتیجه گیری

شاید در پروژه‌های کوچک، سازماندهی استایل‌ها خیلی مسئله‌ی حائز اهمیتی نباشد اما وقتی که پروژه کمی بزرگ و پیچیده می‌شود، سازماندهی کدها و نحوه‌ی آن به طور کلی در سه چیز خیلی موثر خواهد بود؛ یک در مدت زمانی که صرف نوشتن کد و Refactoring می‌شود، دو میزان حجم کدی که باید نوشته شود و سه زمانی که باید مرورگر صرف بارگذاری کدها کند. اینجاست که وجود روشی مثل BEM به شدت احساس می‌شود. البته انتخاب با شماست که در بین متدهای موجود کدامیک را به کار بگیرید یا اینکه حتی متد خودتان را بنویسید.

منبع

5 دیدگاه برای “معرفی روش BEM برای نامگذاری کلاس‎ها

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *