จัดตำแหน่งใน Flexbox ด้วยพลานุภาพของ Auto Margins
Flexbox เป็นหนึ่งสิ่งมหัศจรรย์ในยุครุ่งเรืองของ CSS อีลีเมนต์ไหนที่ใส่เป็น display: flex
หรือ inline-flex
ก็เปรียบเสมือนเป็นกล่อง ถ้ากล่องวางแนวนอนของในกล่องก็ไหลตามแนวนอน แต่ถ้าจับตั้งเสียของในกล่องก็จะวางซ้อนกันในแนวดิ่งนั่นเอง
เราทราบกันดีว่าการไหลของอีลีเมนต์ในกล่องจะมีทิศทางตามแกนหลักของมัน (main axis) เช่นถ้าวางกล่องแนวนอน แกนหลักก็จะเป็นแกนนอน เมื่อมีแกนหลักย่อมมีแกนตั้งได้ฉากกับแกนหลักเรียกว่า cross axis โดยเราสามารถจัดวางตำแหน่งของอีลีเมนต์ได้ทั้งสองแกน หากแต่แกนหลักนี่หละคือตัวปัญหาจึงเป็นที่มาของบทความนี้
มาตรฐาน Flex ไม่มี justify-self
กำหนดให้หน้าเพจของเรามีก้อน 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
1#container {2 display: flex;3 justify-content: center;4 background: #37bc9b;5 height: 200px;6}78.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}
เช่นเดียวกันกลุ่มของอีลีเมนต์ยังสามารถจัดตำแหน่งตามแกนรอง (cross axis) ได้ด้วยผ่าน align-items
เช่นการจัดกลางตามแนวดิ่งด้วย align-items: center
1#container {2 display: flex;3 justify-content: center;4 align-items: center;5 background: #37bc9b;6 height: 200px;7}
สำหรับแกนรองยังมี align-content และ align-self โดย align-self จะอนุญาตให้เราสามารถกำหนดตำแหน่งแยกตัวของแต่ละอีลีเมนต์ให้ผิดแผกไปจากชาวบ้านได้ เช่นให้กล่องหมายเลข 1 วางตำแหน่งตนให้อยู่ที่จุดเริ่มต้นของแกนรองผ่าน align-self: flex-start
1#box1 {2 align-self: flex-start;3}
คำถามครับ แล้วถ้าเราอยากให้กล่องหมายเลข 1 อยู่ชิดมุมซ้ายบน หรืออีกนัยคือให้กล่องจัดตำแหน่งตามแกนหลักไปอยู่ที่จุดเริ่มต้นจะทำได้หรือไม่?
หลายคนอาจคิดว่าการจัดเดี่ยวตามแนวแกนรองนั้นเรามี align-self
งั้นถ้าจัดตามแกนหลักก็ใช้ justify-self
ซิ นั่นเป็นคำตอบที่ผิดครับ เพราะ justify-self
เป็นมาตรฐานของ CSS Box Alignment Module
พลานุภาพของ margin กับ flexbox
เคล็ดการแก้ปัญหานี้อยู่ที่ Auto Margins เมื่อเราระบุ margin เป็น auto ในทิศทางใด พฤติกรรมสัตว์ป่าของมันจะเริ่มปรากฎด้วยการกลืนกินและครอบครองพื้นที่ว่างในทิศทางนั้น ๆ เช่นเราระบุ margin-left: auto
จะทำให้เกิดการจองพื้นที่ด้านซ้ายไว้
สมมติเราต้องการสร้าง navbar ให้เมนูส่วนอื่นอยู่ชิดซ้ายเว้นปุ่มลอคอินที่ให้อยู่ชิดขวา เราสามารถใช้ auto margins แก้ปัญหานี้ได้ ดังนี้
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) เราจึงผลักเฉพาะกล่องสุดท้ายไปไว้ขวาสุด
1#container {2 display: flex;3 background: #37bc9b;4 height: 200px;5}67.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}1718#box3 {19 margin-left: auto;20}
กลับมาที่ปัญหาเดิมของเรา หากเราต้องการให้กล่องหมายเลข 1 จากรูปด้านล่างนี้อยู่ชิดมุมซ้ายบน ด้วยพลานุภาพของ auto margins เราสามารถแก้ปัญหานี้ได้อย่างไร?
ง่ายมากครับ ตอนนี้แกนหลักของเราอีลีเมนต์อยู่ตรงกลาง เราอยากให้กล่องแรกไปชิดซ้าย เราต้องจองพื้นที่ด้านขวาของกล่องเอาไว้ จึงต้องใส่ margin-right: auto
1#box1 {2 align-self: flex-start;3 margin-right: auto;4}
Auto margins นั้นไม่จำเป็นต้องใช้กับแกนหลักเท่านั้น การนำมันมาใช้กับ flex items ก็เหมือนกับการใช้กับ block เหตุนี้ auto margins จึงใช้กับแกนรองได้เช่นกัน
ตอนต้นของบนความเราใช้ justify-content: center
และ align-items: center
เพื่อกำหนดให้อีลีเมนต์อยู่กลางจอ
หากเราต้องการให้อีลีเมนต์ต่าง ๆ อยู่กลางจอด้วยและกระจายตามแนวนอนด้วย เราไม่ต้องมานั่งใส่ justify-content
และ align-items
ให้วุ่นวาย เพียงใช้ margin: auto
ชีวิตก็ดี๊ดีขึ้น
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}
flexbox และ absolute position
margin: auto
ช่วยให้อีลีเมนต์จัดกลางจอ กรณีที่จำนวนอีลีเมนต์เป็นเลขคี่ อีลีเมนต์ตัวกลางจะอยู่หน้าจอพอดี
ทว่าการใช้ margin: auto
ไม่การันตีเสมอไปว่าตัวกลางจะอยู่ตรงกลางเสมอ หากอีลีเมนต์ตัวอื่นขนาดไม่เท่ากันทุกอย่างก็พังพินาศ
นอกจาก auto margins แล้ว absolute position ก็เป็นอีกหนึ่งปัจจัยแห่งการเคลื่อนย้ายตำแหน่งของ flex items ได้ด้วย เราจึงสามารถใช้ position: absolute
เพื่อเคลื่อนกล่องหมายเลข 2 ให้อยู่ตรงกลางหน้าจออย่างแท้จริง
1#container {2 display: flex;3 align-items: center;4 position: relative;5 background: #37bc9b;6 height: 200px;7}89.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}1920#box1 {21 width: 100px;22}2324#box2 {25 position: absolute;26 left: 50%;27 transform: translate(-50%, 0);28}
รู้จัก space-evenly ใน CSS Box Alignment Module
เมื่อพูดถึงการจัดอีลีเมนต์ตามแนวนอน การจัดกลาง จัดชิดซ้ายหรือขวา อาจไม่ใช่คำตอบสุดท้าย บางครั้งเราอยากจัดตำแหน่งให้แต่ละกล่องมีช่องว่างเว้นไว้เท่า ๆ กัน เราจึงเลือก justify-content: space-around
มาแก้ปัญหา
1#container {2 display: flex;3 align-items: center;4 justify-content: space-around;5 background: #37bc9b;6 height: 200px;7}
space-around
จะเพิ่มช่องว่างด้านซ้ายและขวาของแต่ละกล่องเท่า ๆ กัน หากช่องว่างด้านซ้ายของกล่องหมายเลข 1 มีขนาด 1 หน่วย ด้านซ้ายของมันก็จะมีขนาด 1 หน่วยเช่นกัน สำหรับกล่องหมายเลข 2 ก็เป็นเช่นเดียวกับกล่องหมายเลข 1
เมื่อพิจารณาตรงนี้ปัญหาจึงเกิดขึ้น ด้านขวาของกล่องแรกเพิ่มช่องว่าง 1 หน่วย แต่ด้านซ้ายของกล่องสองก็เพิ่มอีก 1 หน่วย ผลลัพธ์ที่ตาเห็นจึงกลายเป็นด้านชิดขอบจอมีขนาด 1 หน่วย แต่พื้นที่ว่างระหว่างกล่องกลับมีค่าเป็น 2 หน่วยแทน นี่ไม่ใช่การกระจายเท่ากันอย่างแท้จริง!
วิธีการแก้ปัญหานี้ง่ายมากด้วยการแสร้งทำเป็นมีกล่องโผล่ขึ้นหน้าและหลังอย่างละใบด้วย ::before
และ ::after
แล้วเปลี่ยนจาก space-around
ที่เพิ่มช่องว่างด้านขอบเป็นการใช้ space-between
ที่มีช่องว่างเพราะด้านที่ติดกันของกล่อง เพียงเท่านี้ก็เป็นอันเสร็จพิธี
1#container {2 display: flex;3 align-items: center;4 justify-content: space-between;5 background: #37bc9b;6 height: 200px;7}89#container::before {10 content: '';11}1213#container::after {14 content: '';15}
แม้วิธีนี้จะดูดิบเถื่อนไปหน่อย แต่มันก็ได้ผลใช่ไหมหละ! ข่าวดีก็คือ CSS Box Alignment Module มีมาตรฐานสำหรับการกระจายแบบเท่ากันเช่นที่ว่าผ่าน justify: space-evenly
ส่วนข่าวร้ายหนะรึ มันยังใช้ไม่ได้ในทุกเบราเซอร์นั่นเอง โถคุณพระ
1#container {2 display: flex;3 align-items: center;4 justify-content: space-evenly;5 background: #37bc9b;6 height: 200px;7}
สรุป
การใช้งาน 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
- สรุป
- เอกสารอ้างอิง