รูปแบบการก่ออิฐ CSS เท่านั้น


134

ฉันจำเป็นต้องใช้รูปแบบการก่ออิฐค่อนข้างทำงาน อย่างไรก็ตามด้วยเหตุผลหลายประการฉันไม่ต้องการใช้ JavaScript เพื่อทำมัน

ตารางของคอลัมน์หลายคอลัมน์ของความสูงที่แตกต่างกัน

พารามิเตอร์:

  • องค์ประกอบทั้งหมดมีความกว้างเท่ากัน
  • องค์ประกอบมีความสูงที่ไม่สามารถคำนวณฝั่งเซิร์ฟเวอร์ (รูปภาพพร้อมข้อความจำนวนมาก)
  • ฉันสามารถอยู่กับคอลัมน์จำนวนคงที่ถ้าฉันต้อง

มีวิธีการแก้ปัญหาที่น่ารำคาญนี้ที่ทำงานในเบราว์เซอร์ที่ทันสมัยคุณสมบัติcolumn-count

ปัญหาของการแก้ปัญหานั้นก็คือการเรียงลำดับองค์ประกอบในคอลัมน์:

เริ่มต้นจากกล่องซ้ายสุดด้านบนพวกเขามีหมายเลข 1 ถึง 4 ตรงลงกล่องด้านบนสุดในคอลัมน์ถัดไปคือ 5 และอื่น ๆ

ในขณะที่ฉันต้องการองค์ประกอบที่จะสั่งซื้อในแถวอย่างน้อยประมาณ:

เริ่มต้นจากกล่องซ้ายสุดด้านบนพวกเขามีหมายเลข 1 ถึง 6 ตรงข้าม แต่เนื่องจากกล่อง 5 เป็นกล่องที่สั้นที่สุดที่อยู่ด้านล่างกล่องที่ 7 เนื่องจากมีลักษณะของการอยู่ในแถวที่สูงกว่ากล่องถัดไปทางด้านซ้ายสุด

แนวทางที่ฉันได้ลองแล้วไม่ได้ผล:

ตอนนี้ฉันสามารถเปลี่ยนการเรนเดอร์ฝั่งเซิร์ฟเวอร์และเรียงลำดับรายการใหม่โดยหารจำนวนรายการด้วยจำนวนคอลัมน์ แต่มันซับซ้อนข้อผิดพลาดง่าย (ขึ้นอยู่กับวิธีที่เบราว์เซอร์ตัดสินใจแบ่งรายการเป็นคอลัมน์) ดังนั้นฉันต้องการ เพื่อหลีกเลี่ยงถ้าเป็นไปได้

มีเวทเฟล็กบ็อกซ์แบบใหม่ที่ทำให้สิ่งนี้เป็นไปได้หรือไม่?


7
ไม่สามารถคิดวิธีที่ไม่ขึ้นอยู่กับความสูงที่กำหนดไว้ล่วงหน้า หากคุณพิจารณา JS อีกครั้งให้ดูที่stackoverflow.com/questions/13518653/ …โดยที่ฉันใช้วิธีการแก้ปัญหาที่ค่อนข้างง่าย
Gabriele Petrioli

1
@Gaby กำลังใช้งานโซลูชันของคุณตอนนี้ขอบคุณ!
Pekka

4
ฉันรู้ว่าคุณพูด CSS-only ฉันแค่อยากจะบอกว่า Masonry ไม่ต้องการ jQuery อีกต่อไป - minified library นั้นมีขนาดต่ำกว่า 8kbและสามารถเริ่มต้นได้ด้วย html เพียงอย่างเดียว เพียงเพื่อการอ้างอิงjsfiddle.net/wp7kuk1t
ฉันได้รับรหัส

1
หากคุณสามารถกำหนดความสูงขององค์ประกอบล่วงหน้าได้โดยการทราบความสูงของบรรทัดขนาดตัวอักษร (คุณจะต้องแสดงแบบอักษรที่เฉพาะเจาะจงและทำการคำนวณที่ฉลาด) ความสูงของภาพความสูงของจุดยอดขอบและการขยายตัว ทำเช่นนี้. มิฉะนั้นคุณไม่สามารถทำได้โดยใช้ CSS เท่านั้น คุณสามารถใช้บางอย่างเช่น PhantomJS เพื่อแสดงผลแต่ละองค์ประกอบล่วงหน้าและรับความสูงขององค์ประกอบนั้น แต่จะมีการเพิ่มค่าใช้จ่าย / เวลาแฝงที่สำคัญ
ทิโมธี Zorn

คำตอบ:


157

flexbox

เลย์เอาต์แบบไดนามิกไม่สามารถใช้ได้กับเฟล็กบ็อกซ์อย่างน้อยก็ไม่เป็นไปอย่างสะอาดและมีประสิทธิภาพ

Flexbox เป็นระบบโครงร่างหนึ่งมิติ ซึ่งหมายความว่าสามารถจัดเรียงรายการตามแนวนอนหรือแนวตั้ง รายการยืดหยุ่นจะถูกคุมขังในแถวหรือคอลัมน์

ระบบกริดที่แท้จริงคือสองมิติซึ่งหมายความว่ามันสามารถจัดเรียงรายการตามแนวนอนและแนวตั้ง ไอเท็มเนื้อหาสามารถขยายข้ามแถวและคอลัมน์พร้อมกันซึ่งไอเท็มแบบยืดหยุ่นไม่สามารถทำได้

นี่คือเหตุผลที่ flexbox มีความจุ จำกัด สำหรับการสร้างกริด นอกจากนี้ยังเป็นเหตุผลว่าทำไม W3C ได้มีการพัฒนาเทคโนโลยี CSS3 อื่นกริดเค้าโครง


row wrap

ในภาชนะดิ้นกับflex-flow: row wrapรายการดิ้นต้องตัดใหม่แถว

ซึ่งหมายความว่ารายการดิ้นไม่สามารถห่อภายใต้รายการอื่นในแถวเดียวกัน

โปรดสังเกตว่าdiv # 3ตัดด้านล่างdiv # 1อย่างไรการสร้างแถวใหม่ มันไม่สามารถห่อใต้div # 2ได้

ดังนั้นเมื่อรายการไม่สูงที่สุดในแถวพื้นที่สีขาวยังคงอยู่ทำให้เกิดช่องว่างที่ไม่น่าดู


column wrap

หากคุณเปลี่ยนไปflex-flow: column wrapใช้เลย์เอาต์ที่คล้ายกับกริดสามารถทำได้มากกว่า อย่างไรก็ตามคอนเทนเนอร์ทิศทางของคอลัมน์มีปัญหาที่อาจเกิดขึ้นได้สี่อย่างจากค้างคาว:

  1. ดิ้นรายการไหลในแนวตั้งไม่ใช่แนวนอน (เช่นที่คุณต้องการในกรณีนี้)
  2. คอนเทนเนอร์ขยายในแนวนอนไม่ใช่แนวตั้ง (เช่นเค้าโครง Pinterest)
  3. มันต้องใช้ภาชนะที่จะมีความสูงคงที่เพื่อให้รายการทราบว่าจะห่อ
  4. ขณะที่เขียนนี้ก็มีความบกพร่องในเบราว์เซอร์ที่สำคัญทั้งหมดที่ภาชนะที่ไม่ขยายเพื่อรองรับคอลัมน์เพิ่มเติม

ดังนั้นคอนเทนเนอร์ทิศทางคอลัมน์จึงไม่ใช่ตัวเลือกในกรณีนี้และในหลายกรณี


CSS Grid ที่มีขนาดรายการไม่ได้กำหนด

เค้าโครงกริดจะเป็นทางออกที่สมบูรณ์แบบสำหรับปัญหาของคุณหากความสูงต่างๆของไอเท็มเนื้อหาสามารถกำหนดล่วงหน้าได้ ข้อกำหนดอื่น ๆ ทั้งหมดอยู่ในความสามารถของ Grid

ต้องรู้จักความกว้างและความสูงของรายการกริดเพื่อปิดช่องว่างด้วยรายการที่อยู่รอบ ๆ

ดังนั้น Grid ซึ่งเป็น CSS ที่ดีที่สุดที่มีให้สำหรับการสร้างเลย์เอาต์แบบก่ออิฐในแนวนอนก็ยังขาดอยู่ในกรณีนี้

ในความเป็นจริงจนกระทั่งเทคโนโลยี CSS มาพร้อมกับความสามารถในการปิดช่องว่างโดยอัตโนมัติ CSS โดยทั่วไปไม่มีวิธีแก้ปัญหา บางสิ่งเช่นนี้อาจต้องมีการปรับแต่งเอกสารใหม่ดังนั้นฉันไม่แน่ใจว่าจะมีประโยชน์หรือมีประสิทธิภาพเพียงใด

คุณจะต้องมีสคริปต์

โซลูชัน JavaScript มักใช้การกำหนดตำแหน่งแบบสัมบูรณ์ซึ่งจะลบไอเท็มเนื้อหาออกจากโฟลว์เอกสารเพื่อจัดเรียงใหม่โดยไม่มีช่องว่าง นี่คือสองตัวอย่าง:


CSS Grid พร้อมกำหนดขนาดรายการ

สำหรับเลย์เอาต์ที่ความกว้างและความสูงของไอเท็มเนื้อหาเป็นที่รู้จักกันนี่เป็นเลย์เอาต์แบบก่ออิฐแนวนอนใน CSS บริสุทธิ์:

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}
<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

การสาธิต jsFiddle


มันทำงานอย่างไร

  1. สร้างคอนเทนเนอร์กริดระดับบล็อก ( inline-gridจะเป็นตัวเลือกอื่น ๆ )
  2. grid-auto-rowsคุณสมบัติกำหนดความสูงของแถวที่สร้างขึ้นโดยอัตโนมัติ ในตารางนี้แต่ละแถวมีความสูง 50px
  3. grid-gapคุณสมบัติคือชวเลขเป็นและgrid-column-gap grid-row-gapกฎนี้ตั้งค่าช่องว่าง 10px ระหว่างรายการกริด (มันใช้ไม่ได้กับพื้นที่ระหว่างรายการและภาชนะ)
  4. grid-template-columnsคุณสมบัติกำหนดความกว้างของคอลัมน์ที่กำหนดไว้อย่างชัดเจน

    repeatโน้ตกำหนดรูปแบบของคอลัมน์ (หรือแถว) ซ้ำ

    auto-fillฟังก์ชั่นบอกตารางต้องไปเข้าแถวเป็นคอลัมน์หลายคน (หรือแถว) ที่เป็นไปได้โดยไม่ต้องล้นภาชนะ (สิ่งนี้สามารถสร้างพฤติกรรมที่คล้ายคลึงกับเค้าโครงแบบยืดหยุ่นflex-wrap: wrap)

    minmax()ฟังก์ชั่นตั้งค่าต่ำสุดและสูงสุดช่วงขนาดสำหรับแต่ละคอลัมน์ (หรือแถว) ในโค้ดด้านบนความกว้างของแต่ละคอลัมน์จะต้องมีอย่างน้อย 30% ของคอนเทนเนอร์และสูงสุดของพื้นที่ว่างใด ๆ ที่มีอยู่

    frหน่วยหมายถึงส่วนของพื้นที่ว่างในภาชนะตารางที่ มันเปรียบได้กับflex-growคุณสมบัติของ flexbox

  5. ด้วยgrid-rowและspanเรากำลังบอกรายการตารางว่ามีกี่แถวที่ควรขยาย


รองรับเบราว์เซอร์สำหรับ CSS Grid

  • Chrome - การสนับสนุนอย่างเต็มที่ตั้งแต่วันที่ 8 มีนาคม 2017 (รุ่น 57)
  • Firefox - การสนับสนุนอย่างเต็มที่ตั้งแต่วันที่ 6 มีนาคม 2017 (รุ่น 52)
  • Safari - การสนับสนุนอย่างเต็มที่ตั้งแต่วันที่ 26 มีนาคม 2017 (เวอร์ชั่น 10.1)
  • Edge - รองรับเต็มที่ตั้งแต่วันที่ 16 ตุลาคม 2017 (รุ่น 16)
  • IE11 - ไม่รองรับสเป็คปัจจุบัน รองรับรุ่นที่ล้าสมัย

นี่คือภาพที่สมบูรณ์: http://caniuse.com/#search=grid


ฟีเจอร์การซ้อนทับกริดใน Firefox

ในเครื่องมือ Firefox dev เมื่อคุณตรวจสอบกริดคอนเทนเนอร์จะมีไอคอนกริดเล็ก ๆ ในการประกาศ CSS เมื่อคลิกจะแสดงโครงร่างของตารางของคุณบนหน้า

รายละเอียดเพิ่มเติมได้ที่นี่: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts


5
คำตอบที่ยอดเยี่ยม! ด้วยวิธีแก้ปัญหา "CSS Grid ที่มีการกำหนดขนาดรายการ" เป็นไปได้ไหมที่จะแสดงความสูงของเซลล์เป็นเปอร์เซ็นต์ของความกว้างของเซลล์ สิ่งนี้จะเป็นประโยชน์สำหรับการแสดงภาพโดยที่รู้จักอัตราส่วนภาพ เราต้องการรักษาอัตราส่วนภาพไว้ตลอดเวลา
Oliver Joseph Ash

ขอบคุณ สำหรับอัตราส่วนภาพสำหรับวิธีการ "เปอร์เซ็นต์ความกว้างของเซลล์" (เคล็ดลับการขยายเต็มเปอร์เซ็นต์) ไม่น่าเชื่อถือใน Grid หรือ Flexbox สำหรับเรื่องนั้น ดูที่นี่และที่นี่ @OliverJosephAsh
Michael Benjamin

ใช่ฉันกำลังอ้างถึงเคล็ดลับการแพ็ดเปอร์เซ็นต์ เป็นไปได้ไหมที่จะใช้วิธีการแก้ปัญหาใด ๆ ร่วมกับวิธีแก้ปัญหาเลย์เอาต์ของคุณ? สำหรับบริบทเรากำลังมองหาที่จะใช้ CSS เท่านั้นรูปแบบการก่ออิฐสำหรับภาพบนunsplash.com
Oliver Joseph Ash

1
น่าเสียดายที่ใช้ JS สำหรับสิ่งนี้ไม่ใช่ตัวเลือก เราต้องการเปิดใช้งานการเรนเดอร์ฝั่งเซิร์ฟเวอร์เพื่อประสิทธิภาพและเหตุผล SEO ซึ่งหมายความว่าต้องแสดงเลย์เอาต์ก่อนที่ JavaScript ฝั่งไคลเอ็นต์จะดาวน์โหลดแยกวิเคราะห์และดำเนินการ เนื่องจากสิ่งนี้ดูเหมือนจะเป็นไปไม่ได้ฉันเดาว่าเราจะต้องทำการแลกเปลี่ยนที่ไหนสักแห่งตามแนว! ขอบคุณสำหรับความช่วยเหลือของคุณ :-)
Oliver Joseph Ash

1
การอัพเดทข้อมูลจำเพาะ:ในเฟล็กบ็อกซ์ตอนนี้เปอร์เซ็นต์ระยะขอบและช่องว่างภายในจะถูกตั้งค่าเพื่อแก้ไขขนาดอินไลน์ของคอนเทนเนอร์อีกครั้ง drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin

13

เทคนิคนี้ถูกค้นพบเมื่อเร็ว ๆ นี้ที่เกี่ยวข้องกับ flexbox: https://tobiasahlin.com/blog/masonry-with-css/

บทความมีเหตุผลสำหรับฉัน แต่ฉันไม่ได้พยายามใช้ดังนั้นฉันไม่รู้ว่ามีคำเตือนใด ๆ นอกเหนือจากที่ระบุไว้ในคำตอบของไมเคิล

นี่คือตัวอย่างจากบทความที่ทำให้การใช้งานของสถานที่ให้บริการรวมกับorder:nth-child

ตัวอย่างสแต็ก

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 960px;
  
  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>


ก่อนอื่นลิงก์เพียงคำตอบคือคำตอบที่ไม่ดีเช่นเมื่อลิงก์ดังกล่าวตายดังนั้นคำตอบจึงเป็นเช่นนั้น ประการที่สองถ้าคุณอ่านคำตอบที่ได้รับอย่างระมัดระวังมากขึ้นคุณจะพบสิ่งที่กล่าวถึงในลิงค์ของคุณครอบคลุม พูดง่ายๆคือมีข้อ จำกัด มากมายในการใช้ Flexbox เพียงอย่างเดียวเว้นแต่ว่าสิ่งที่กล่าวไว้ข้างต้นไม่ใช่ปัญหา
Ason

3
ฉันได้อ่านคำตอบที่ได้รับการยอมรับอย่างละเอียด - คุณจะเห็นความคิดเห็นบางส่วนที่ฉันเพิ่มไว้ที่ด้านล่าง วิธีการ flexbox ที่ฉันเชื่อมโยงไปนั้นมีเอกลักษณ์และไม่ครอบคลุมโดยคำตอบนั้น ฉันไม่ต้องการที่จะทำซ้ำบทความเพราะมันซับซ้อนมากและบทความทำงานได้ดีในการครอบคลุม
Oliver Joseph Ash

สิ่งเดียวที่การเชื่อมโยงนั้นแตกต่างกันคือการใช้ประโยชน์ของorderทรัพย์สินทำให้ดูเหมือนว่าสิ่งของไหลจากซ้ายไปขวา แต่ถึงกระนั้นเคล็ดลับหลักของมันก็คือflex-flow: column wrapซึ่งได้กล่าวถึงในคำตอบข้างต้น นอกจากนี้มันไม่สามารถไหลของไอเท็มในแบบที่ก่อขึ้นจริงเช่นถ้าไอเท็มที่ 3 และ 4 จะพอดีในคอลัมน์ที่ 3 พวกมันจะไอเท็มที่เชื่อมโยงไม่ได้ แต่อย่างที่ฉันพูดมันทั้งหมดขึ้นอยู่กับข้อกำหนดและในกรณีนี้ (คำถามนี้) มันไม่ทำงานตามที่ขอ
Ason

นอกจากนี้มันไม่เกี่ยวกับ"คุณไม่ต้องการที่จะทำซ้ำบทความ"คำตอบควรมีส่วนที่สำคัญของรหัสดังนั้นมันจะยังคงถูกต้องเมื่อทรัพยากรที่เชื่อมโยงตาย
Ason

3
ฉันอัปเดตแล้วเพิ่มตัวอย่างจากบทความ + การอัปโหลด
Ason
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.