จัดตำแหน่งใน Flexbox ด้วยพลานุภาพของ Auto Margins

Nuttavut Thongjor

Flexbox เป็นหนึ่งสิ่งมหัศจรรย์ในยุครุ่งเรืองของ CSS อีลีเมนต์ไหนที่ใส่เป็น display: flex หรือ inline-flex ก็เปรียบเสมือนเป็นกล่อง ถ้ากล่องวางแนวนอนของในกล่องก็ไหลตามแนวนอน แต่ถ้าจับตั้งเสียของในกล่องก็จะวางซ้อนกันในแนวดิ่งนั่นเอง

Flex Direction

เราทราบกันดีว่าการไหลของอีลีเมนต์ในกล่องจะมีทิศทางตามแกนหลักของมัน (main axis) เช่นถ้าวางกล่องแนวนอน แกนหลักก็จะเป็นแกนนอน เมื่อมีแกนหลักย่อมมีแกนตั้งได้ฉากกับแกนหลักเรียกว่า cross axis โดยเราสามารถจัดวางตำแหน่งของอีลีเมนต์ได้ทั้งสองแกน หากแต่แกนหลักนี่หละคือตัวปัญหาจึงเป็นที่มาของบทความนี้

มาตรฐาน Flex ไม่มี justify-self

กำหนดให้หน้าเพจของเรามีก้อน HTML ดังนี้

HTML
1<div id="container">
2 <div class="box" id="box1">1</div>
3 <div class="box" id="box2">2</div>
4 <div class="box" id="box3">3</div>
5</div>

เราสามารถจัดตำแหน่งของอีลีเมนต์ตามทิศทางการไหลในแกนหลักได้ผ่าน justify-content เช่นจัดสิ่งของให้อยู่กลางด้วย justify-content: center

CSS
1#container {
2 display: flex;
3 justify-content: center;
4 background: #37bc9b;
5 height: 200px;
6}
7
8.box {
9 width: 50px;
10 height: 50px;
11 line-height: 50px;
12 background: #434a54;
13 color: #f5f7fa;
14 border: 1px solid #ccd1d9;
15 text-align: center;
16}

Center of main axis

เช่นเดียวกันกลุ่มของอีลีเมนต์ยังสามารถจัดตำแหน่งตามแกนรอง (cross axis) ได้ด้วยผ่าน align-items เช่นการจัดกลางตามแนวดิ่งด้วย align-items: center

CSS
1#container {
2 display: flex;
3 justify-content: center;
4 align-items: center;
5 background: #37bc9b;
6 height: 200px;
7}

Center of cross axis

สำหรับแกนรองยังมี align-content และ align-self โดย align-self จะอนุญาตให้เราสามารถกำหนดตำแหน่งแยกตัวของแต่ละอีลีเมนต์ให้ผิดแผกไปจากชาวบ้านได้ เช่นให้กล่องหมายเลข 1 วางตำแหน่งตนให้อยู่ที่จุดเริ่มต้นของแกนรองผ่าน align-self: flex-start

CSS
1#box1 {
2 align-self: flex-start;
3}

align-self

คำถามครับ แล้วถ้าเราอยากให้กล่องหมายเลข 1 อยู่ชิดมุมซ้ายบน หรืออีกนัยคือให้กล่องจัดตำแหน่งตามแกนหลักไปอยู่ที่จุดเริ่มต้นจะทำได้หรือไม่?

หลายคนอาจคิดว่าการจัดเดี่ยวตามแนวแกนรองนั้นเรามี align-self งั้นถ้าจัดตามแกนหลักก็ใช้ justify-self ซิ นั่นเป็นคำตอบที่ผิดครับ เพราะ justify-self เป็นมาตรฐานของ CSS Box Alignment Module

พลานุภาพของ margin กับ flexbox

เคล็ดการแก้ปัญหานี้อยู่ที่ Auto Margins เมื่อเราระบุ margin เป็น auto ในทิศทางใด พฤติกรรมสัตว์ป่าของมันจะเริ่มปรากฎด้วยการกลืนกินและครอบครองพื้นที่ว่างในทิศทางนั้น ๆ เช่นเราระบุ margin-left: auto จะทำให้เกิดการจองพื้นที่ด้านซ้ายไว้

สมมติเราต้องการสร้าง navbar ให้เมนูส่วนอื่นอยู่ชิดซ้ายเว้นปุ่มลอคอินที่ให้อยู่ชิดขวา เราสามารถใช้ auto margins แก้ปัญหานี้ได้ ดังนี้

HTML
1<div id="container">
2 <div class="box" id="box1">Articles</div>
3 <div class="box" id="box2">Courses</div>
4 <div class="box" id="box3">Login</div>
5</div>

ค่าเริ่มต้นอีลีเมนต์จะถูกจัดให้อยู่ที่จุดเริ่มต้น (flex-start) เราจึงผลักเฉพาะกล่องสุดท้ายไปไว้ขวาสุด

CSS
1#container {
2 display: flex;
3 background: #37bc9b;
4 height: 200px;
5}
6
7.box {
8 width: 50px;
9 height: 50px;
10 padding: 0 10px;
11 line-height: 50px;
12 background: #434a54;
13 color: #f5f7fa;
14 border: 1px solid #ccd1d9;
15 text-align: center;
16}
17
18#box3 {
19 margin-left: auto;
20}

Auto Margins

กลับมาที่ปัญหาเดิมของเรา หากเราต้องการให้กล่องหมายเลข 1 จากรูปด้านล่างนี้อยู่ชิดมุมซ้ายบน ด้วยพลานุภาพของ auto margins เราสามารถแก้ปัญหานี้ได้อย่างไร?

align-self

ง่ายมากครับ ตอนนี้แกนหลักของเราอีลีเมนต์อยู่ตรงกลาง เราอยากให้กล่องแรกไปชิดซ้าย เราต้องจองพื้นที่ด้านขวาของกล่องเอาไว้ จึงต้องใส่ margin-right: auto

CSS
1#box1 {
2 align-self: flex-start;
3 margin-right: auto;
4}

left-top corner

Auto margins นั้นไม่จำเป็นต้องใช้กับแกนหลักเท่านั้น การนำมันมาใช้กับ flex items ก็เหมือนกับการใช้กับ block เหตุนี้ auto margins จึงใช้กับแกนรองได้เช่นกัน

ตอนต้นของบนความเราใช้ justify-content: center และ align-items: center เพื่อกำหนดให้อีลีเมนต์อยู่กลางจอ

Center of cross axis

หากเราต้องการให้อีลีเมนต์ต่าง ๆ อยู่กลางจอด้วยและกระจายตามแนวนอนด้วย เราไม่ต้องมานั่งใส่ justify-content และ align-items ให้วุ่นวาย เพียงใช้ margin: auto ชีวิตก็ดี๊ดีขึ้น

CSS
1.box {
2 width: 50px;
3 height: 50px;
4 line-height: 50px;
5 background: #434a54;
6 color: #f5f7fa;
7 border: 1px solid #ccd1d9;
8 text-align: center;
9 margin: auto;
10}

margin auto

flexbox และ absolute position

margin: auto ช่วยให้อีลีเมนต์จัดกลางจอ กรณีที่จำนวนอีลีเมนต์เป็นเลขคี่ อีลีเมนต์ตัวกลางจะอยู่หน้าจอพอดี

margin auto

ทว่าการใช้ margin: auto ไม่การันตีเสมอไปว่าตัวกลางจะอยู่ตรงกลางเสมอ หากอีลีเมนต์ตัวอื่นขนาดไม่เท่ากันทุกอย่างก็พังพินาศ

margin auto problem

นอกจาก auto margins แล้ว absolute position ก็เป็นอีกหนึ่งปัจจัยแห่งการเคลื่อนย้ายตำแหน่งของ flex items ได้ด้วย เราจึงสามารถใช้ position: absolute เพื่อเคลื่อนกล่องหมายเลข 2 ให้อยู่ตรงกลางหน้าจออย่างแท้จริง

CSS
1#container {
2 display: flex;
3 align-items: center;
4 position: relative;
5 background: #37bc9b;
6 height: 200px;
7}
8
9.box {
10 width: 50px;
11 height: 50px;
12 line-height: 50px;
13 background: #434a54;
14 color: #f5f7fa;
15 border: 1px solid #ccd1d9;
16 text-align: center;
17 margin: auto;
18}
19
20#box1 {
21 width: 100px;
22}
23
24#box2 {
25 position: absolute;
26 left: 50%;
27 transform: translate(-50%, 0);
28}

Absolute positioning

รู้จัก space-evenly ใน CSS Box Alignment Module

เมื่อพูดถึงการจัดอีลีเมนต์ตามแนวนอน การจัดกลาง จัดชิดซ้ายหรือขวา อาจไม่ใช่คำตอบสุดท้าย บางครั้งเราอยากจัดตำแหน่งให้แต่ละกล่องมีช่องว่างเว้นไว้เท่า ๆ กัน เราจึงเลือก justify-content: space-around มาแก้ปัญหา

CSS
1#container {
2 display: flex;
3 align-items: center;
4 justify-content: space-around;
5 background: #37bc9b;
6 height: 200px;
7}

space-around

space-around จะเพิ่มช่องว่างด้านซ้ายและขวาของแต่ละกล่องเท่า ๆ กัน หากช่องว่างด้านซ้ายของกล่องหมายเลข 1 มีขนาด 1 หน่วย ด้านซ้ายของมันก็จะมีขนาด 1 หน่วยเช่นกัน สำหรับกล่องหมายเลข 2 ก็เป็นเช่นเดียวกับกล่องหมายเลข 1

เมื่อพิจารณาตรงนี้ปัญหาจึงเกิดขึ้น ด้านขวาของกล่องแรกเพิ่มช่องว่าง 1 หน่วย แต่ด้านซ้ายของกล่องสองก็เพิ่มอีก 1 หน่วย ผลลัพธ์ที่ตาเห็นจึงกลายเป็นด้านชิดขอบจอมีขนาด 1 หน่วย แต่พื้นที่ว่างระหว่างกล่องกลับมีค่าเป็น 2 หน่วยแทน นี่ไม่ใช่การกระจายเท่ากันอย่างแท้จริง!

วิธีการแก้ปัญหานี้ง่ายมากด้วยการแสร้งทำเป็นมีกล่องโผล่ขึ้นหน้าและหลังอย่างละใบด้วย ::before และ ::after แล้วเปลี่ยนจาก space-around ที่เพิ่มช่องว่างด้านขอบเป็นการใช้ space-between ที่มีช่องว่างเพราะด้านที่ติดกันของกล่อง เพียงเท่านี้ก็เป็นอันเสร็จพิธี

CSS
1#container {
2 display: flex;
3 align-items: center;
4 justify-content: space-between;
5 background: #37bc9b;
6 height: 200px;
7}
8
9#container::before {
10 content: '';
11}
12
13#container::after {
14 content: '';
15}

space equally

แม้วิธีนี้จะดูดิบเถื่อนไปหน่อย แต่มันก็ได้ผลใช่ไหมหละ! ข่าวดีก็คือ CSS Box Alignment Module มีมาตรฐานสำหรับการกระจายแบบเท่ากันเช่นที่ว่าผ่าน justify: space-evenly ส่วนข่าวร้ายหนะรึ มันยังใช้ไม่ได้ในทุกเบราเซอร์นั่นเอง โถคุณพระ

CSS
1#container {
2 display: flex;
3 align-items: center;
4 justify-content: space-evenly;
5 background: #37bc9b;
6 height: 200px;
7}

distributed alignment values

สรุป

การใช้งาน flexbox นั้นยืดหยุ่นมากและไม่จำกัดเพียงแค่การจัดทิศทางตามแกนเท่านั้น เรายังสามารถจัดตำแหน่งของแต่ละ flex items ด้วยการใช้ auto margin และ absolute positioning ได้เช่นกัน

นอกเหนือจาก flexbox ที่เป็นการจัดตำแหน่งในทิศทางเดียวแล้ว CSS ยังมีการจัดอีลีเมนต์ในสองมิติด้วยนั่นคือมาตรฐานของ CSS Grid Layout เพื่อน ๆ ที่สนใจสามารถอ่านเพิ่มเติมได้จาก CSS Grid Layout คืออะไร? รู้จักมาตรฐานการออกแบบเลย์เอาท์ใน 2 มิติกันเถอะ!

เอกสารอ้างอิง

Center and bottom-align flex items. Retrieved Mar, 26, 2018, from https://stackoverflow.com/questions/36191516/center-and-bottom-align-flex-items

W3C CSS Flexible Box Layout Module Level 1. Retrieved Mar, 26, 2018, https://www.w3.org/TR/css-flexbox-1/#auto-margins

W3C Distributed Alignment: the stretch, space-between, space-around, and space-evenly keywords. Retrieved Mar, 26, 2018, https://www.w3.org/TR/css-flexbox-1/#auto-margins

MichaelB. (2017) _Equal space between flex items. Retrieved Mar, 26, 2018, from https://stackoverflow.com/questions/45134400/equal-space-between-flex-items

MichaelB. (2015) _Methods for Aligning Flex Items along the Main Axis. Retrieved Mar, 26, 2018, https://stackoverflow.com/questions/32551291/in-css-flexbox-why-are-there-no-justify-items-and-justify-self-properties/33856609

สารบัญ

สารบัญ

  • มาตรฐาน Flex ไม่มี justify-self
  • พลานุภาพของ margin กับ flexbox
  • flexbox และ absolute position
  • รู้จัก space-evenly ใน CSS Box Alignment Module
  • สรุป
  • เอกสารอ้างอิง