Babel Coder

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

beginner

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

Flex Direction

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

สารบัญ

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

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

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

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

#container {
  display: flex;
  justify-content: center;
  background: #37BC9B;
  height: 200px;
}

.box {
  width: 50px;
  height: 50px;
  line-height: 50px; 
  background: #434A54;
  color: #F5F7FA;
  border: 1px solid #CCD1D9;
  text-align: center; 
}

Center of main axis

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

#container {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #37BC9B;
  height: 200px;
}

Center of cross axis

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

#box1 {
  align-self: flex-start;
}

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 แก้ปัญหานี้ได้ ดังนี้

<div id="container">
  <div class="box" id="box1">Articles</div>
  <div class="box" id="box2">Courses</div>
  <div class="box" id="box3">Login</div>
</div>

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

#container {
  display: flex;
  background: #37BC9B;
  height: 200px;
}

.box {
  width: 50px;
  height: 50px;
  padding: 0 10px;
  line-height: 50px; 
  background: #434A54;
  color: #F5F7FA;
  border: 1px solid #CCD1D9;
  text-align: center; 
}

#box3 {
  margin-left: auto;
}

Auto Margins

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

align-self

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

#box1 {
  align-self: flex-start;
  margin-right: auto;
}

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 ชีวิตก็ดี๊ดีขึ้น

.box {
  width: 50px;
  height: 50px;
  line-height: 50px; 
  background: #434A54;
  color: #F5F7FA;
  border: 1px solid #CCD1D9;
  text-align: center; 
  margin: auto;
}

margin auto

flexbox และ absolute position

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

margin auto

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

margin auto problem

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

#container {
  display: flex;
  align-items: center;
  position: relative;
  background: #37BC9B;
  height: 200px;
}

.box {
  width: 50px;
  height: 50px;
  line-height: 50px; 
  background: #434A54;
  color: #F5F7FA;
  border: 1px solid #CCD1D9;
  text-align: center; 
  margin: auto;
}

#box1 {
  width: 100px;
}

#box2 {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
}

Absolute positioning

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

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

#container {
  display: flex;
  align-items: center;
  justify-content: space-around;
  background: #37BC9B;
  height: 200px;
}

space-around

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

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

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

#container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #37BC9B;
  height: 200px;
}

#container::before {
  content: ''
}

#container::after {
  content: ''
}

space equally

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

#container {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  background: #37BC9B;
  height: 200px;
}

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

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

Michael_B. (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


แสดงความคิดเห็นของคุณ


No any discussions