ماژول Flexible Box Layout که بهاختصار Flexbox نامیده میشود، یک ابزار قدرتمند برای طرحبندی صفحه (layout)
با استفاده از CSS است که بسیاری از مسائل و مشکلات مربوط به layout را از بین میبرد و کارهایی را که در گذشته
انجامشان سخت بود، بسیار ساده کرده است. Flexbox یک ابزار layout تکبعدی است؛ یعنی میتواند چینش عناصر را یا
به صورت افقی و یا به صورت عمودی (و نه در هر دو جهت) انجام دهد. عنصری که چیدمانش با استفاده از Flexbox انجام
شود یک کانتینر flex و عناصر درون آن عنصر را آیتمهای flex میگویند.
مفاهیم پایه در مورد Flexbox
قبل از هر چیز، باید با مفاهیم و اصطلاحات مربوط به ماژول Flexbox آشنا شویم.
جهت یا Direction
یک کانتینرِ flex دارای یک جهت است که با استفاده از پراپرتی flex-direction تعیین میشود. این پراپرتی میتواند
یکی از چهار مقدار row، row-reverse، column و column-reverse را دریافت کند. row و column بهترتیب به معنای
سطر و ستون هستند اما کارکرد این مقادیر به جهتگیری محتوای عنصر بستگی دارد که میتواند راستبهچپ (RTL) یا
چپبهراست (LTR) باشد.
برای عنصری که جهتگیری چپبهراست دارد، مقدار row به معنای چینش در راستای افقی از سمت چپبهراست است و
row-reverse به معنای چینش در راستای افقی در جهت راستبهچپ است. مقادیر column و column-reverse نیز به
ترتیب، به معنای چینش در راستای عمودی از بالا به پایین و پایین به بالا هستند.
برای عنصری که جهتگیری راستبهچپ دارد، مقدار row به معنای چینش در راستای افقی از سمت راستبهچپ است و
row-reverse به معنای چینش در راستای افقی در جهت چپبهراست است. مقادیر column و column-reverse نیز به
ترتیب، به معنای چینش در راستای عمودی از بالا به پایین و پایین به بالا هستند.
برای تبدیل یک عنصر به یک کانتینر flex کافیست پراپرتی display را برای آن عنصر روی مقدار flex تنظیم کنیم. به
این ترتیب، یک کانتینر flex از نوع block-level ایجاد میشود. در صورت تمایل، میتوانیم با تخصیص مقدار
inline-flex به پراپرتی display یک کانتینر flex از نوع inline ایجاد کنیم.
تصویر زیر تأثیر استفاده از هر یک از مقادیر row، row-reverse، column و column-reverse را برای یک کانتینر flex
که دارای جهتگیری چپبهراست است، نشان میدهد.
در مورد یک کانتینر flex با جهتگیری راستبهچپ، کافیست در تصویر بالا جای برچسبهای row و row-reverse را عوض
کنیم.
محورها یا Axes
یک مفهوم دیگر که به جهت(direction) کانتینر flex نیز مربوط است، مفهوم محورها (axes) است. محور اصلی (main
axis) از روی جهت تعیینشده توسط پراپرتی flex-direction مشخص میشود و محور فرعی (cross axis) عمود بر محور
اصلی در نظر گرفته میشود. برای مثال، اگر پراپرتی flex-direction دارای مقدار row باشد، محور اصلی در راستای
افقی و از چپبهراست است و محور فرعی در راستای عمودی و از بالا به پایین است. تصویر زیر این دو محور را نشان
میدهد.
یک مثال ساده از Flexbox
همانطور که قبلاً هم اشاره شد، برای ایجاد یک کانتینر flex و تبدیل عناصر فرزند آن عنصر به آیتمهای flex کافیست
از مقدار flex برای پراپرتی display استفاده کنیم. به علاوه، گفتیم که جهت یک flexbox را نیز با استفاده از
پراپرتی flex-direction تعیین میکنیم. با بهکارگیری این موارد در کد زیر، یک مثال بسیار ساده از یک flexbox
ایجاد شده است.
در اینجا چند نکته قابل مشاهده است. اول اینکه ارتفاع کانتینر از روی محتوایش مشخص میشود. به علاوه، آیتمها به
صورت پشت سر هم قرار گرفتهاند و هیچ فاصلهای بین آنها وجود ندارد. برای ایجاد فاصله بین آیتمها میتوانیم از
یک پراپرتی با نام gap استفاده کنیم. و البته از همه مهمتر اینکه آیتمهای flex فارغ از اینکه از نوع عناصر
block باشند یا inline، به صورت پشت سر هم در راستای محور اصلی یا main axis چیده میشوند.
تعیین اندازه
اگر آیتمهای flex در کانتینر flex جا نشوند، چه اتفاقی رخ میدهد؟ برای اینکه مرزهای کانتینر را بهتر ببینید،
در مثال بعدی مقداری padding برای کانتینر در نظر گرفتهایم.
در اینجا کانتینر ما دارای عرض 500px است و شامل سه آیتم است که هر یک 300px عرض دارند. قاعدتاً باید سرریز
(overflow) رخ دهد اما همانطور که میبینید، چنین اتفاقی رخ نداده و در عوض، آیتمها تغییر اندازه دادهاند تا
درون کانتینر جا شوند.
اما گاهی اوقات عناصر تا آنجا که ممکن است کوچک میشوند اما باز هم درون کانتیر جا نمیشوند و اینجاست که
overflow رخ میدهد. مثال زیر این وضعیت را نشان میدهد.
خوب، پس تا اینجا میدانیم که اگر آیتمهای flex درون کانتینر flex جا نشوند، تا جایی که ممکن باشد، تغییر
اندازه میدهند تا درون کانتینر جا شوند و وقتی دیگر امکان کوچکتر شدن نداشته باشند، از کانتینر خود سرریز
میکنند. برای حل این مشکل و به طور کلی برای کنترل سایز آیتمهای flex چند پراپرتی وجود دارد که در ادامه آنها
را معرفی میکنیم. بعضی از این پراپرتیها روی کانتینر و بعضی دیگر روی آیتمها اعمال میشوند.
پراپرتی flex-wrap
یک راهحل ممکن برای مشکل قبلی، استفاده از پراپرتی flex-wrap است. وقتی مقدار این پراپرتی را برای کانتینر روی
wrap تنظیم کنیم، آیتمهای flex بهجای سرریز، به خط بعدی منتقل میشوند. تأثیر استفاده از مقدار wrap برای
پراپرتی flex-wrap را در کد زیر میبینید.
اگر این کد را اجرا کنید، خواهید دید که آیتمها بزرگ نمیشوند و مقداری فضای خالی در کانتینر دیده میشود. این
رفتار را میتوان با استفاده از یک پراپرتی با نام flex-grow کنترل کرد. این پراپرتی نه روی کانتینر بلکه روی
آیتمهای flex اعمال میشود. مقدار پیشفرض این پراپرتی صفر است که همانطور که گفته شد و دیدیم، باعث میشود که
آیتمها بزرگ نشوند. پراپرتی flex-grow دارای یک مقیاس نسبی است. یعنی اگر همهی آیتمها دارای مقدار برابری
باشند، همگی به یک نسبت بزرگ میشوند تا کانتینر پر شود. مثال زیر را ببینید.
اگر این کد را اجرا کنید، خواهید دید که همهی آیتمهای flex به نسبت یکسانی بزرگ میشوند تا کانتینر پر شود.
حالا فرض کنید بخواهیم آیتم دوم دو برابر آیتمهای اول و سوم بزرگ شود. کافیست از مقادیر متفاوتی مانند آنچه در
کد زیر دیده میشود، برای پراپرتی flex-grow استفاده کنیم.
همانطور که در تصویر زیر میبینید، در نتیجهی اجرای این کد، آیتم دوم با نسبت دو برابری نسبت به آیتمهای اول و
سوم بزرگ میشود.
توجه داشته باشید که این امر به این معنا نیست که اندازهی آیتم دوم دو برابر آیتم اول یا سوم باشد، بلکه از
فضای موجود، سهم آیتم دوم نسبت به سایر آیتمها دو برابر است. در ابتدا هر سه آیتم دارای عرض 100px هستند. از
200px فضای باقیمانده، 100px سهم آیتم دوم است و آیتمهای اول و سوم هر یک 50px بزرگ میشوند.
پراپرتی flex-shrink
قبلاً دیدیم که وقتی سایز آیتمهای flex از کانتینر بیشتر باشد، مرورگر سعی میکند با کاهش سایز آیتمهنا آنها
را در کانتینر جا دهد. این کاهش سایز در حالت پیشفرض، به طور یکسان روی آیتمها اعمال میشود. اما همانطور که
نسبت بزرگشدن آیتمها را با استفاده از پراپرتی flex-grow تعیین کردیم، میتوانیم از یک پراپرتی مشابه با نام
flex-shrink برای کنترل نسبت کوچکشدن آیتمها استفاده کنیم.
مقدار پیشفرض پراپرتی flex-shrink برابر با 1 است که باعث میشود همهی آیتمها با نسبت یکسانی کوچک شوند. در کد
زیر، مقدار این پراپرتی را برای آیتم دوم به 2 تغییر دادهایم.
CSS
.item2{backgroun: orangered;flex-shrink: 2;}
در این مثال، عرض هر آیتم 200px است و بنابراین، مجموعاً 600px فضا برای این سه آیتم لازم است اما کانتینر ما
فقط 300px فضا دارد. در حالت پیشفرض، هر سه آیتم به اندازهی یکسان (یعنی 100px) کوچک میشوند تا در کانتینر جا
شوند. اما با توجه به این که مقدار پراپرتی flex-shrink را برای آیتم دوم روی 2 تنظیم کردهایم، 150px از آیتم
دوم و 75px از هر یک از دو آیتم دیگر کم میشود.
پراپرتی flex-basis
پراپرتی flex-basis سایز اولیهی یک آیتم flex را در راستای محور اصلی (main axis) قبل از اعمال پراپرتیهای
flex-grow و flex-shrink تعیین میکند. در مثالهای قبلی، ما از پراپرتی width برای تعیین سایز آیتمهای flex
استفاده کردیم اما بهتر است این کار را با استفاده از پراپرتی flex-basis انجام دهیم که با سایر پراپرتیهای
مربوط به Flexbox سازگاری بیشتری دارد.
تا اینجا دیدیم که چطور آیتمهای flex را در یک کانتینر flex سایزدهی میکنیم. در این بخش قصد دریم در مورد تراز
(alignment) آیتمها صحبت کنیم و ببینیم چه اتفاقی رخ میدهد وقتی که همهی آیتمها در راستای محور فرعی (cross
axis) هماندازه نیستند. به علاوه، در مورد فاصلهگذاری بین آیتمها و نحوهی توزیع فضای باقیمانده صحبت خواهیم
کرد.
پراپرتی justify-content
پراپرتی justify-content روی کانتینر flex اعمال میشود و نحوهی تراز آیتمهای کانتینر را در راستای محور اصلی
تعیین میکند. مقدار پیشفرض این پراپرتی flex-start است که باعث میشود آیتمها با شروع از ابتدای محور اصلی به
صورت پشت سر هم قرار بگیرند. حالا اینکه محور اصلی کدام است و ابتدا و انتهای ان کدام سمت، چیزی است که از روی
writing mode تعیین میشود. جدول زیر، مقادیر ممکن برای پراپرتی justify-content و نتیجهی هر مقدار را برای یک
عنصر دارای یک زبان چپبهراست (مثل انگلیسی) و با پراپرتی flex-direction: row نشان میدهد.
مقدار
توضیح
مثال
flex-start
آیتمها با شروع از ابتدای محور اصلی، به صورت پشت سر هم قرار میگیرند.
flex-end
آیتمها یکی پس از دیگری در انتهای محور اصلی قرار میگیرند.
center
آیتمها به صورت پشت سر هم و در مرکز محور اصلی قرار میگیرند.
space-between
فاصلهی بین آیتمها ماکزیمم میشود. به این ترتیب که اولین آیتم در نقطهی شروع محور اصلی و آخرین آیتم در
نقطهی پایان محور اصلی قرار میگیرند و سایر آیتمها به صورت یکنواخت بین این دو آیتم توزیع میشوند.
space-around
مشابه مقدار space-between است با این تفاوت که در ابتدا و انتها کمی فضا وجود دارد.
space-evenly
مشابه مقدار space-around است با این تفاوت که فاصله در همهی جهات همهی آیتمها یکسان است.
پراپرتی align-items
پراپرتی align-items هم روی کانتینر flex اعمال میشود و نحوهی تراز آیتمها را در راستای محور فرعی (cross
axis) کنترل میکند. مقدار پیشفرض این پراپرتی stretch است که باعث میشود آیتمها در راستای محور فرعی کشیده
شوند تا فضای کانتینر را پر کنند؛ البته در حالی که به محدودیتهای مربوط به عرض و ارتفاع پایبند هستند. مقادیر
مختلف برای پراپرتی align-item در جدول زیر ارائه شده است.
مقدار
توضیح
مثال
stretch
آیتمها به اندازهای کشیده میشوند که فضای موجود در راستای محور فرعی را پر کنند. یک آیتم بیشتر از
میزان height یا max-height کشیده نمیشود.
flex-start
آیتمها با شروع از محور فرعی تراز میشوند.
flex-end
آیتمها با شروع از انتهای محور فرعی تراز میشوند.
center
آیتمها نسبت به مرکز محور فرعی تراز میشوند.
baseline
آیتمها نسبت به baseline مربوط به متنشان تراز میشوند.
پراپرتی align-content
وقتی بیش از یک سطر داشته باشیم (یا بیش از یک ستون در یک flex ستونی) و مقداری فضای اضافی در راستای محور فرعی
موجود باشد، با استفاده از پراپرتی align-content میتوانیم نحوهی توزیع این فضا را کنترل کنیم. مقدار پیشفرض
این پراپرتی stretch است که باعث میشود آیتمها در راستای محور فرعی کشیده شوند. مقادیر ممکن برای پراپرتی
align-content در جدول زیر ارائه شده است.
مقدار
توضیح
مثال
stretch
آیتمها به اندازهای کشیده میشوند که فضای موجود در راستای محور فرعی را پر کنند. یک آیتم بیشتر از
میزان height یا max-height کشیده نمیشود.
flex-start
سطرها را نسبت به ابتدای محور فرعی تراز میکند.
flex-end
سطرها را نسبت به انتهای محور فرعی تراز میکند.
center
سطرها را نسبت به مرکز محور فرعی تراز میکند.
space-between
فاصلهی بین سطرها را ماکزیمم میکند.
space-around
بین سطرها فاصل ایجاد میکند به نحوی که در ابتدا و انتها نصف این فاصله باشد.
space-evenly
بین همهی سطرها فاصلهی مساوی قرار میدهد.
پراپرتی align-self
دیدیم که پراپرتی align-items همهی آیتمها را تراز میکند. حالا اگر بخواهیم برای یکی ازآیتمها مقداری را
تنظیم کنیم که مقدار تنظیمشده توسط align-items را بازنویسی یا لغو (override) کند، باید از پراپرتی align-self
برای آن آیتم استفاده کنیم.
تغییر ترتیب آیتمها با پراپرتی order
در حالت پیشفرض، چیدمان آیتمها بر اساس دو عامل انجام میشود: یکی ترتیب آیتمها در HTML و دیگری مقدار
پراپرتی flex-direction.
ترتیب حاصل از این دو عامل را میتوان با استفاده از پراپرتی order برای آیتمها تغییر داد. وقتی از این پراپرتی
برای یک آیتم flex استفاده کنیم، ترتیب وقوع آن آیتم را تعیین میکند. این امکان هم وجود دارد که برای چند آیتم
مقدار یکسانی را برای پراپرتی order تعیین کنیم که در این صورت، برای تعیین ترتیب وقوع این آیتمها به ترتیب
آنها در HTML رجوع میشود.
در مورد پراپرتی order یک نکته در ارتباط با Accessibility وجود دارد که باید آن را مد نظر قرار دهیم:
پراپرتی order فقط روی ترتیب عناصر در صفحهنمایش اثر میگذارد و مثلاً یک screen reader عناصر را به همان
ترتیبی که در سورس آمده، میخواند. این امر باعث میشود که نمایش آیتمها با ترتیب نمایش آنها در صفحهنمایش
هماهنگ نباشد که میتواند گیجکننده باشد. بنابراین، بهتر است حتی المقدور از پراپرتی order استفاده نکنیم.
مثالهایی از Flexbox
با تکنولوژی Flexbox و پراپرتیهای مربوط به آن آشنا شدیم و دیدیم که چطور امکان تعیین layout در یک بعد را برای
ما ممکن میکند. در پایان این درس، دو مثال کاربردی از Flexbox ارائه میدهیم تا این تکنولوژی را بهتر درک کنید.
مرکزیسازی مطلق
مسئلهی مرکزیسازی مطلق (absolute centering) یعنی هم به صورت افقی و هم به صورت عمودی، با استفاده از Flexbox
به سادگی حل میشود. کافیست مقدار پراپرتیهای justify-content و align-items را روی center تنظیم کنیم.
اگر کد بالا را اجرا کنید، خواهید دید که باکس درونی هم به صورت افقی و هم به صورت عمودی در مرکز باکس بیرونی
قرار گرفته است.
طرحبندی صفحه
با تودرتو کردن باکسهای flex میتوانیم انواع طرحبندیها (layouts) را ایجاد کنیم. در مثال زیر، یک طرحبندی
کامل برای یک صفحه با استفاده از Flexbox ایجاد شده است.