مقدمه

Box Model یک مفهوم بسیار مهم و کلیدی در CSS است که به رفتار عناصر از نظر قرارگیری در صفحه و فضای اشغال‌شده توسط باکس‌ها مربوط است. برای درک مفهوم Box Model لازم است که ابتدا با برخی از مفاهیم و پراپرتی‌های مربوط به این موضوع آشنا شوبم. قبل از هر چیز، با باکس‌های Block و Inline و تفاوت بین آنها آشنا می‌شویم. سپس، در مورد ابعاد عناصر و پراپرتی‌های width و height صحبت می‌کنیم. بعد از آن، با مفاهیم Margin و Padding و پراپرتی‌های مربوط به آنها آشنا می‌شویم و بالاخره با بررسی دقیق‌تر تفاوت بین باکس‌های Inline و Block پازل مربوط به مفهوم Box Model کامل می‌شود و می‌توانیم به یک تفسیر جامع از این مفهوم دست پیدا کنیم.

باکس‌های Inline و Block

همانطور که در درس قبل هم اشاره شد، عناصر HTML معادل باکس‌های CSS هستند. یعنی CSS هر عنصر موجود در صفحه را به صورت یک باکس مستطیلی رندر می‌کند. باکسِ یک عنصر یا Inline است یا Block و این موضوعی است که از روی مقدار پیش‌فرض یک پراپرتی با نام display‌ مشخص می‌شود. یعنی هر عنصری که مقدار پیش‌فرض پراپرتی display برای آن برابر با inline باشد، یک عنصر Inline است و هر عنصری که مقدار پیش‌فرض پراپرتی display برای آن برابر با block باشد، یک عنصر Block است.

به طور خلاصه، تفاوت باکس‌های Inline و Block از این قرار است:

عناصر یا باکس‌های Block همیشه در یک خط یا سطر جدید قرار می‌گیرند و کل عرض آن سطر (عرض عنصر والد) را اشغال می‌کنند و هر عنصری که بعد از یک باکس Block بیاید، باید در سطر یا خط بعدی قرار گیرد. اما عناصر یا باکس‌های inline لزوماً از یک خط جدید شروع نمی‌شوند و از فضای موجود در سطر جاری، تنها به اندازه‌ی محتوای خود اشغال می‌کنند.

باکس‌های Inline و Block
تفاوت عناصر Block و Inline

بنابراین، عناصری مانند پاراگراف‌ها (p)، هدینگ‌ها (h1, …, h6)، جداول (table) و div که همواره از یک خط جدید شروع شده و فارغ از محتوای خود، کل عرض عنصر والد خود را اشغال می‌کنند، باکس‌ها یا عناصر Block هستند و عناصری مانند لینک‌ها (a)، تصاویر (img) و span که تنها به اندازه‌ی محتوای خود از فضای خط جاری را اشغال می‌کنند و از یک خط جدید نیز شروع نمی‌شوند، عناصر یا باکس‌های Inline هستند. اجازه دهید با ارائه‌ی یک مثال، تفاوت باکس‌های Block و Inline را در عمل ببینیم. در کد زیر، فریم عناصر Inline با رنگ سبز و فریم عناصر Block با رنگ قرمز نمایش داده می‌شود:

 Copy Icon CSS
h1, h2, p{
  border: 1px solid red;
}

a, span, strong{
  border: 1px solid green;
}

همانطور که می‌بینید، باکس‌های عناصر Block یعنی h1، h2 و p از ابتدا تا انتهای یک سطر یا به عبارت دیگر، صد درصد عرض سطر جاری را اشغال می‌کنند و ارتفاع این باکس‌ها نیز بر حسب محتوا تنظیم می‌شود. اما عناصر Inline این مثال یعنی a، span و strong تنها به اندازه‌ی محتوای خود از عرض و ارتفاع ممکن، فضا می‌گیرند.

نکته‌ی مهم در ارتباط با باکس‌های Inline و Block این است که Box Model روی این دو گروه از عناصر، معنای متفاوتی دارد و به دو شکل متفاوت تفسیر می‌شود.

تبدیل باکس‌های Block و Inline به یکدیگر

همانطور که در بالا اشاره شد، این پراپرتی display است که Block یا Inline بودن یک عنصر را مشخص می‌کند. پس، با تغیییر مقدار پیش‌فرض پراپرتی display برای یک عنصر، می‌توان عناصر Block و Inline را به یکدیگر تبدیل کرد.

می‌دانیم که عنصر li از نوع Block است و به همین دلیل است که هر یک از آیتم‌های یک لیست در یک خط مجزا نمایش داده می‌شوند. مثال زیر را ببینید:

 Copy Icon CSS
ul li{
  list-style-type: none;
}

اما اگر مقدار پراپرتی display را برای عنصر li تغییر داده و آن را روی inline تنظیم کنیم، آیتم‌ها مانند عناصر Inline در کنار یکدیگر نمایش داده می‌شوند و فقط زمانی یک آیتم در یک خط جدید نمایش داده می‌شود که فضای کافی برای نمایش آن آیتم در خط قبلی وجود نداشته باشد. مثال زیر را ببینید:

 Copy Icon CSS
ul li{
  list-style-type: none;
  display: inline;
}

تعیین ابعاد باکس‌ها

منظور از ابعاد باکس، عرض و ارتفاع آن است که چند پراپرتی برای تعیین آنها وجود دارد که مهمترین آنها width و height هستند. البته باید توجه داشته باشید که این پراپرتی‌ها تنها روی عناصر Block اعمال می‌شوند و به‌کارگیری آنها روی عناصر Inline هیچ نتیجه‌ای ندارد و یک عنصر Inline در هر شرایطی، تنها به اندازه‌ی محتوای خود از عرض صفحه یا عنصر والد را اشغال می‌کند.

عناصر Block در حالت پیش‌فرض، کل عرض عنصر والد خود را اشغال می‌کنند. به عبارت دیگر، مقدار پراپرتی width‌ برای این عناصر برابر با 100% است. ارتفاع این عناصر نیز از روی محتوای آنها تعیین می‌شود که این به این معناست که مقدار پیش‌فرض پراپرتی height برای عناصر Block برابر با auto است. برای تعیین مقدار دلخواه به عنوان عرض و ارتفاع عنصر، باید مقادیر مورد نظر را به پراپرتی‌های width و height اختصاص دهیم.

 Copy Icon CSS
#child{
  height: 50%;
  width: 75%;
}

جداقل و حداکثر ابعاد

علاوه بر تخصیص یک سایز ثابت و مشخص به یک عنصر، امکان تعیین حداقل یا حداکثر عرض و ارتفاع نیز برای عنصر وجود دارد. اگر باکسی داشته باشیم که محتوای آن متغیر باشد و بخواهیم آن باکس همیشه دارای یک ارتفاع حداقلی باشد، می توانیم از پراپرتی min-height بدین منظور استفاده کنیم. در این صورت، باکس مورد نظر دارای آن ارتفاع کمینه خواهد بود و با افزایش احتمالی محتوا، مقدار ارتفاع باکس نیز افزایش می‌یابد تا بتواند محتوا را در بر گیرد.

در مثال زیر، دو باکس داریم که ارتفاع حداقل برای هر دوی آنها روی 100px تنظیم شده است. باکس اول با وجودی که محتوایی ندارد، با ارتفاع 100px نمایش داده می‌شود؛ چون این مقداری است که ما به عنوان حداقل ارتفاع تعیین کرده‌ایم. اما باکس سمت راست محتوایی دارد که ارتفاع 100px جوابگوی ان نیست و لذا ارتفاع به میزان لازم افزایش می‌یابد.

 Copy Icon CSS
.box{
  border: 5px solid darkblue;
  min-height: 100px;
  width: 200px;
}

این روش برای مواردی که مقدار محتوای درون یک باکس متغیر است، مفید بوده و مانع از بروز سرریز محتوا می‌شود.

پراپرتی max-width نیز کاربرد خاص خودش را دارد و در مواردی که بخواهیم ترتیبی دهیم که عرض باکس یک عنصر هرگز از یک مقدار مشخصی بیشتر نشود، می‌توانیم از آن استفاده کنیم. به عنوان یک مثال از کاربرد پراپرتی max-with فرض کنید پراپرتی width را برای یک عکس روی مقدار 100% تنظیم کنیم و عرض طبیعی عکس از عرض باکس کانتینرش کمتر باشد. در این صورت، عکس مورد نظر اجباراً کشیده‌تر شده و بزرگتر می‌شود و این امر می‌تواند باعث افت کیفیت عکس بشود. اما اگر به جای width از پراپرتی max-width استفاده کنیم، این مشکل رخ نمی‌دهد.

مفاهیم Padding و Margin

یکی دیگر از مؤلفه‌هایی که برای درک مفهوم Box Model و محاسبه‌ی فضای اشغال‌شده توسط عناصر باید با آن آشنا شویم، مفاهیم Margin و Padding هستند. منظور از Margin فاصله یا حاشیه‌ای است که بین یک عنصر و عناصر مجاورش وجود دارد و Padding فاصله‌ی بین محتوا و فریم (border) عنصر است. در این بخش، با پراپرتی‌های تنظیم‌کننده‌ی Margin و Padding آشنا می‌شویم.

مفهوم Padding و پراپرتی‌های مربوط به آن

فاصله‌ی بین محتوای یک عنصر و فریم (border) آن را Padding می‌گوییم و این فاصله را می‌توان با استفاده از پراپرتی‌های padding-top، padding-right، padding-bottom و padding-left از چهار طرف تعیین کرد. البته یک پراپرتی اختصاری با نام padding نیز وجود دارد که امکان تنظیم هر چهار پراپرتی مذکور را در قالب تنها یک دستور فراهم می‌کند. مقدار این پراپرتی‌ها می‌تواند یک مقدار طولی نامنفی باشد و یا یک مقدار بر حسب درصد که نسبت به عرض محتوا یعنی مقدار پراپرتی width سنجیده می‌شود. مثال زیر را ببینید:

 Copy Icon CSS
p{
  border: 1px solid gray;
  padding-top: 10px;
  padding-right: 0;
  padding-bottom: 15px;
  padding-left: 10%;
}

اما همانطور که گفتیم، به جای این پراپرتی‌ها می‌توانیم از پراپرتی اختصاری padding برای تنظیم همزمان Padding از چهار طرف عنصر، استفاده کنیم. این پراپرتی می‌تواند از یک تا چهار مقدار را دریافت کند:

  • تخصیص یک مقدار به پراپرتی padding به این معناست که آن مقدار برای میزان Padding از هر چهار طرف عنصر در نظر گرفته شود.
  • تخصیص دو مقدار به پراپرتی padding به این معناست که مقدار اول برای Padding از بالا و پایین یعنی پراپرتی‌های padding-top و padding-bottom در نظر گرفته شود و مقدار دوم برای Padding از راست و چپ یعنی پراپرتی‌های padding-right و padding-left.
  • تخصیص سه مقدار به پراپرتی padding به این معناست که مقدار اول برای padding-top و مقدار دوم برای padding-right و padding-left و مقدار سوم برای padding-bottom در نظر گرفته شود.
  • تخصیص چهار مقدار به پراپرتی padding به این معناست که مقادیر به‌ترتیب برای Padding از بالا، راست، پایین و چپ در نظر گرفته شوند.

مثال بالا را می‌توانیم با استفاده از پراپرتی اختصاری padding به صورت زیر بازنویسی کنیم:

 Copy Icon CSS
p{
  border: 1px solid gray;
  padding: 10px 0 15px 10%;
}

مفهوم Margin و پراپرتی‌های مربوط به آن

مفهوم Margin بیانگر فضای اطراف باکس عنصر است. این فضا باعث می‌شود که یک حاشیه برای عنصر مورد نظر ایجاد شود و لذا سایر عناصر از این عنصر فاصله بگیرند. بر خلاف پراپرتی‌های مربوط به Padding که تنها قادر به دریافت مقادیر نامنفی هستند، پراپرتی‌های مربوط به Margin می‌تواند مقادیر مثبت یا منفی را دریافت کنند. تعیین اعداد منفی به عنوان مقدار این پراپرتی‌ها می‌تواند باعث ایجاد تداخل (overlap) بین عنصر با سایر عناصر و محتوای صفحه شود.

برای تنظیم مقدار Margin برای یک عنصر، می‌توانیم از پراپرتی‌های margin-top، margin-right، margin-bottom و margin-left برای تنظیم میزان Margin از چهار جهت استفاده کنیم. این پراپرتی‌ها می‌توانند یک مقدار طولی، یک مقدار بر حسب درصد و یا مقدار auto را دریافت کنند. مقادیر درصدی نسبت به عرض عنصر والد یا کانتینر سنجیده می‌شوند.

اگر بخواهیم یک عنصر را در مرکز صفحه یا به طور کلی در مرکز عنصر والد خود قرار دهیم، می‌توانیم مقدار پراپرتی‌های margin-left و margin-right را برابر با auto قرار دهیم. مثال زیر را ببینید:

 Copy Icon CSS
.center{
  margin: auto;
  background: lime;
  width: 66%;
}

.outside{
  margin: 3rem 0 0 -3rem;
  background: cyan;
  width: 66%;
}

مفهوم Margin Collapsing

برای درک کامل مفهوم Margin باید با مفهومی به نام Margin Collapsing آشنا باشیم. این مفوم بیان می‌کند که اگر حاشیه‌های تعیین شده برای دو عنصر با استفاده از پراپرتی‌های مربوط به Margin به گونه‌ای باشد که این حاشیه‌ها با هم تماس داشته باشند، دو حاشیه با هم ترکیب شده و یک مقدار که مقدار حاشیه‌ی بزرگتر است، بدست می‌آید. البته این به شرطی است که مقدار Margin برای هر دو عنصر مثبت باشد اما اگر لااقل یکی از آنها دارای مقدار منفی باشد، مقدار منفی از کل کم می‌شود.

در مثال زیر، دو پاراگراف داریم. مقدار Margin برای پاراگراف اول برابر با 50px و برای پاراگراف دوم برابر با 30px است. همانطور که می‌بینید، دو مقدار با هم ادغام شده و مقدار بزرگتر یعنی 50px به عنوان فاصله‌ی بین دو عنصر تعیین شده و نه مجموع آنها.

 Copy Icon CSS
.one{
  margin-bottom: 50px;
}

.two{
  margin-top: 30px;
}

عناصر یا باکس‌های inline-block

مقادیر block و inline تنها مقادیر قابل تخصیص به پراپرتی display نیستند و مقادیر متعدد دیگری وجود دارد که می‌توان به این پراپرتی اختصاص داد. در اینجا قصد داریم به یک مقدار خاص اشاره کنیم که با اعطای آن به display می‌توانیم حالت بینابینی از مقادیر block و inline برای عنصر ایجاد کنیم؛ یعنی عنصر از برخی جهات، همانند عناصر Inline و از برخی جهات دیگر، همانند عناصر Block رفتار کند. این مقدار خاص، inline-block است.

یادآوری می‌کنم که پراپرتی‌های width و height روی عناصر Inline کار نمی‌کنند. حالا اگر بخواهیم یک عنصر همانند عناصر Inline در صفحه نمایش داده شود، یعنی در یک خط جدید نمایش داده نشود (مگر اینکه قبل از آن یک عنصر Block قرار داشته باشد) اما همانند یک عنصر Block از پراپرتی‌های width و height تأثیر بگیرد، کافیست مقدار inline-block را به پراپرتی display آن عنصر اختصاص دهیم. مثال زیر را ببینید:

 Copy Icon CSS
li{
  display: inline-block;
  width: 100px;
  height: 25px;
  background-color: yellow;
  text-align: center;
}

درک مفهوم Box Model

بسیار خوب، حالا با هر آنچه که برای درک Box Model نیاز داریم، آشنا هستیم و می‌توانیم تصویری از این مفهوم کلیدی ارائه کنیم. Box Model مفهومی است که بخش‌های مختلف یک باکس یعنی content، padding، border و margin را تعریف کرده و امکان محاسبه‌ی فضای اشغال‌شده توسط هر باکس را فراهم می‌کند.

Box Model

دیدیم که بخش‌های سازنده‌ی یک باکس عبارتند از:

  • Margin: بیرونی‌ترین بخش باکس عنصر است که به معنای حاشیه‌ی اطراف عنصر یا به عبارت دیگر، فاصله‌ی بین فریم (border) عنصر با فریم عناصر مجاور یا عنصر والد است.
  • Border: فریم یا قاب عنصر است که در حالت پیش‌فرض، نمایش داده نمی‌شود اما می‌توان آن را با ضخامت، رنگ و استایل مورد نظر نمایش داد.
  • Padding: فاصله‌ی بین محتوای عنصر و فریم آن عنصر است که می‌توان مقدار آن را برای چهار سمت عنصر، با استفاده تعیین کرد.
  • Content: محتوای عنصر است که درون باکس عنصر قرار می‌گیرد.

برای درک صحیح مفهوم Box Model باید تفاوت بین عناصر Inline و Block را در نظر داشته باشید:

  • عرض و ارتفاع عناصر Block را می‌توان با استفاده از پراپرتی‌های width و height تعیین کرد و پراپرتی‌های مربوط به Margin و Padding و Border باعث می‌شوند که سایر عناصر از باکس مورد نظر فاصله بگیرند. به عبارت دیگر، این پراپرتی‌ها روی یک عنصر Block هم اعمال می‌شوند و هم توسط عناصر یا باکس‌های مجاور، به رسمیت شناخته می‌شوند.
  • پراپرتی‌های width و height روی عناصر Inline تأثیری ندارند و قابل اعمال نیستند. همچنین، اعمال پراپرتی‌های مربوط به Margin، Padding و Border روی یک باکس Inline باعث می‌شود که سایر باکس‌های Inline از سمت چپ و راست باکس مورد نظر (به صورت افقی) فاصله بگیرند اما از بالا و پایین ‌باکس مورد نظر (به صورت عمودی)، خیر. به عبارت دیگر، این پراپرتی‌ها روی یک عنصر یا باکس Inline اعمال می‌شوند اما مقادیر این پراپرتی‌ها توسط عناصر مجاوری که بالا یا پایین عنصر مورد نظر قرار دارند، نادیده گرفته می‌شوند.

Box Model دارای دو فرم است: یک فرم استاندارد که به طور پیش‌فرض در جریان است و یک فرم آلترناتیو یا جایگزین که می‌توان آن را با فرم اصلی جایگزین کرد. اینجا عامل تعیین‌کننده، یک پراپرتی با نام box-sizing است که می‌تواند یکی از دو مقدار content-box یا border-box را دریافت کند. مقدار content-box معادل فرم استاندارد Box Model است و مقدار border-box معادل فرم آلترناتیو.

فرم استاندارد Box Model

مقدار پیش‌فرض پراپرتی box-sizing برای همه‌ی عناصر برابر با content-box است و این باعث می‌شود که پراپرتی‌های width و height برای بخش Content یا محتوای یک باکس در نظر گرفته شوند. بنابراین، در این حالت، برای محاسبه‌ی فضای اشغال‌شده توسط یک باکس، باید مقادیر Padding و Border را هم به عرض و ارتفاع اضافه کنیم.

فرم استاندارد Nox Model

به عنوان مثال، فضای اشغال شده توسط عنصر div در مثال زیر برابر است با: 410=350+25+25+5+5 در عرض و 210 = 150+25+25+5+5 پیکسل در ارتفاع.

Copy Icon CSS
.box{
  width: 350px;
  height: 250px;
  margin: 10px;
  padding: 25px;
  border: 5px solid black;
}

این همان چیزی است که Standard Box Model نامیده می‌شود.

فرم آلترناتیو Box Model

با تغییر مقدار پراپرتی box-sizing به border-box، می‌توانیم Alternative Box Model را برای آن عنصر فعال کنیم که رویکرد منطقی‌تری را برای محاسبه‌ی فضای اشغالل‌شده توسط عناصر فراهم می‌کند. در این مدل، مقداری که برای width و height تعیین می شود، مقادیر Padding و Border را هم شامل می‌شود.

فرم آلترناتیو Box Model

اگر می‌خواهید همانطور که در بین بسیاری از توسعه‌دهندگان رایج است، از روش آلترناتیو برای همه عناصر استفاده کنید، کافیست از یک انتخابگر universal مانند زیر استفاده کنید:

Copy Icon CSS
*, ::before, ::after{
  box-sizing: border-box;
}

با وجودی که این روش کار می‌کند و باعث می‌شود که فرم آلترناتیو Box Model برای همه‌ی عناصر صفحه فعال شود، اما بهتر است از فرم زیر استفاده کنیم:

Copy Icon CSS
html{
  box-sizing: border-box;
}

*, ::before, ::after{
  box-sizing: inherit;
}

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