Layout pasangan bata hanya CSS

134

Saya perlu menerapkan tata letak batu yang cukup run-off-the-mill. Namun, untuk sejumlah alasan saya tidak ingin menggunakan JavaScript untuk melakukannya.

Kisi beberapa kolom persegi panjang dengan ketinggian berbeda-beda.

Parameter:

  • Semua elemen memiliki lebar yang sama
  • Elemen memiliki tinggi yang tidak dapat dihitung sisi server (gambar ditambah berbagai jumlah teks)
  • Saya bisa hidup dengan jumlah kolom yang tetap jika saya harus

ada solusi sepele untuk ini yang bekerja di browser modern, yang column-countproperti.

Masalah dengan solusi itu adalah bahwa elemen-elemen disusun dalam kolom:

Mulai dari kotak paling kiri atas, mereka diberi nomor 1 hingga 4 lurus ke bawah, kotak paling atas di kolom berikutnya adalah 5, dan seterusnya.

Sementara saya membutuhkan elemen untuk dipesan dalam baris, setidaknya kira-kira:

Mulai dari kotak paling kiri atas, mereka diberi nomor 1 hingga 6 lurus, tetapi karena kotak 5 adalah yang terpendek kotak di bawahnya adalah 7 karena memiliki penampilan berada pada baris yang lebih tinggi daripada kotak berikutnya di paling kiri.

Saya sudah mencoba pendekatan yang tidak berhasil:

Sekarang saya bisa mengubah rendering sisi server dan menyusun ulang item yang membagi jumlah item dengan jumlah kolom, tapi itu rumit, rawan kesalahan (berdasarkan bagaimana browser memutuskan untuk membagi daftar item menjadi kolom), jadi saya ingin untuk menghindarinya jika memungkinkan.

Apakah ada sihir flexbox bermodel baru yang memungkinkan ini terjadi?

Pekka
sumber
7
Tidak dapat memikirkan cara yang tidak bergantung pada ketinggian yang telah ditentukan. Jika Anda mempertimbangkan kembali JS, lihat stackoverflow.com/questions/13518653/... di mana saya mengimplementasikan solusi seperti itu yang cukup sederhana.
Gabriele Petrioli
1
@ Gaby mengimplementasikan solusi Anda sekarang, terima kasih!
Pekka
4
Saya menyadari bahwa Anda mengatakan hanya CSS. Saya hanya ingin menyebutkan bahwa Masonry tidak lagi memerlukan jQuery - pustaka yang diperkecil di bawah 8kb - dan dapat diinisialisasi dengan html saja. Hanya untuk referensi jsfiddle.net/wp7kuk1t
I
1
Jika Anda dapat menentukan ketinggian elemen sebelumnya, dengan mengetahui ketinggian garis, ukuran font (Anda harus menyajikan font tertentu dan melakukan beberapa perhitungan pintar), tinggi gambar, margin dan bantalan, Anda dapat melakukan hal ini. Kalau tidak, Anda tidak dapat melakukan ini hanya menggunakan CSS. Anda juga bisa menggunakan sesuatu seperti PhantomJS untuk pre-render setiap elemen dan mendapatkan ketinggian elemen itu, tetapi akan ada overhead / latensi yang signifikan ditambahkan.
Timothy Zorn

Jawaban:

157

Flexbox

Tata letak batu yang dinamis tidak dimungkinkan dengan flexbox, setidaknya tidak dengan cara yang bersih dan efisien.

Flexbox adalah sistem tata letak satu dimensi. Ini berarti dapat menyelaraskan item di sepanjang garis horizontal atau vertikal. Item fleksibel terbatas pada baris atau kolomnya.

Sistem grid yang sebenarnya adalah dua dimensi, artinya dapat menyelaraskan item di sepanjang garis horizontal DAN vertikal. Item konten dapat menjangkau seluruh baris dan kolom secara bersamaan, yang item fleksibel tidak dapat lakukan.

Inilah sebabnya mengapa flexbox memiliki kapasitas terbatas untuk membangun kisi-kisi. Itu juga alasan mengapa W3C telah mengembangkan teknologi CSS3 lain, Grid Layout .


row wrap

Dalam wadah fleksibel dengan flex-flow: row wrap, item fleksibel harus dibungkus dengan baris baru .

Ini berarti bahwa item fleksibel tidak dapat dibungkus di bawah item lain di baris yang sama .

Perhatikan di atas bagaimana div # 3 membungkus di bawah div # 1 , membuat baris baru. Itu tidak dapat membungkus di bawah div # 2 .

Akibatnya, ketika item bukan yang tertinggi di baris, ruang putih tetap ada, menciptakan celah yang tidak sedap dipandang.


column wrap

Jika Anda beralih ke flex-flow: column wrap, tata letak seperti grid lebih dapat dicapai. Namun, wadah pengarah kolom memiliki empat masalah potensial segera:

  1. Item flex mengalir secara vertikal, bukan horizontal (seperti yang Anda butuhkan dalam hal ini).
  2. Wadah mengembang secara horizontal, bukan vertikal (seperti tata letak Pinterest).
  3. Ini membutuhkan wadah untuk memiliki ketinggian tetap, sehingga barang tahu di mana harus membungkus.
  4. Pada tulisan ini, ia memiliki kekurangan di semua browser utama di mana wadah tidak berkembang untuk mengakomodasi kolom tambahan .

Akibatnya, wadah arah kolom bukan merupakan pilihan dalam kasus ini, dan dalam banyak kasus lainnya.


Kotak CSS dengan dimensi item tidak ditentukan

Layout Kotak akan menjadi solusi sempurna untuk masalah Anda jika berbagai ketinggian item konten dapat ditentukan sebelumnya . Semua persyaratan lain berada dalam kapasitas Grid.

Lebar dan tinggi item kisi harus diketahui untuk menutup celah dengan item di sekitarnya.

Jadi Grid, yang merupakan CSS terbaik yang ditawarkan untuk membangun tata letak batu yang mengalir secara horizontal, gagal dalam hal ini.

Bahkan, sampai teknologi CSS tiba dengan kemampuan untuk secara otomatis menutup kesenjangan, CSS secara umum tidak memiliki solusi. Sesuatu seperti ini mungkin perlu merefleksikan dokumen, jadi saya tidak yakin seberapa berguna atau efisiennya dokumen itu.

Anda membutuhkan skrip.

Solusi JavaScript cenderung menggunakan penentuan posisi absolut, yang menghapus item konten dari aliran dokumen untuk mengaturnya kembali tanpa ada celah. Berikut ini dua contoh:


CSS Grid dengan dimensi item didefinisikan

Untuk tata letak tempat lebar dan tinggi item konten diketahui, berikut ini adalah tata letak pasangan bata yang mengalir secara horizontal di CSS murni:

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>

Demo jsFiddle


Bagaimana itu bekerja

  1. Tetapkan wadah kotak level blok. ( inline-gridakan menjadi pilihan lain)
  2. The grid-auto-rowsproperti menetapkan ketinggian baris secara otomatis. Di kotak ini setiap baris tingginya 50px.
  3. The grid-gapproperti adalah singkatan untuk grid-column-gapdan grid-row-gap. Aturan ini menetapkan kesenjangan 10px antara item kisi. (Ini tidak berlaku untuk area antara item dan wadah.)
  4. The grid-template-columnsproperti menetapkan lebar kolom didefinisikan secara eksplisit.

    The repeatnotasi mendefinisikan pola berulang kolom (atau baris).

    The auto-fillFungsi memberitahu grid untuk berbaris banyak kolom (atau baris) mungkin tanpa meluap wadah. (Ini dapat membuat perilaku yang mirip dengan tata letak fleksibel flex-wrap: wrap.)

    The minmax()fungsi menetapkan minimum dan maksimum rentang ukuran untuk masing-masing kolom (atau baris). Dalam kode di atas, lebar setiap kolom akan menjadi minimum 30% dari wadah dan maksimum ruang kosong apa pun yang tersedia.

    The frUnit mewakili sebagian kecil dari ruang bebas dalam wadah kotak. Ini sebanding dengan flex-growproperti flexbox .

  5. Dengan grid-rowdan spankami memberi tahu item kisi berapa banyak baris yang harus mereka bentangkan.


Dukungan Browser untuk Grid CSS

  • Chrome - dukungan penuh pada 8 Maret 2017 (versi 57)
  • Firefox - dukungan penuh per 6 Maret 2017 (versi 52)
  • Safari - dukungan penuh pada 26 Maret 2017 (versi 10.1)
  • Edge - dukungan penuh pada 16 Oktober 2017 (versi 16)
  • IE11 - tidak ada dukungan untuk spesifikasi saat ini; mendukung versi usang

Inilah gambaran lengkapnya: http://caniuse.com/#search=grid


Fitur overlay kisi keren di Firefox

Di alat pengembang Firefox, ketika Anda memeriksa wadah kotak, ada ikon kotak kecil di deklarasi CSS. Pada klik itu akan menampilkan garis besar grid Anda pada halaman.

Lebih detail di sini: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

Michael Benjamin
sumber
5
Jawaban yang fantastis! Dengan solusi "Kotak CSS dengan dimensi item yang ditentukan", apakah mungkin untuk mengekspresikan ketinggian sel sebagai persentase dari lebar sel? Ini akan berguna untuk menampilkan gambar, di mana rasio aspek diketahui. Kami ingin mempertahankan rasio aspek setiap saat.
Oliver Joseph Ash
Terima kasih. Sehubungan dengan masalah rasio aspek untuk gambar, metode "persentase lebar sel" (trik padding persentase?), Itu tidak dapat diandalkan di Grid, atau Flexbox, dalam hal ini. Lihat di sini dan di sini . @OliverJosephAsh
Michael Benjamin
Ya, saya mengacu pada trik padding persentase. Apakah mungkin untuk menggunakan salah satu solusi dalam kombinasi dengan solusi tata letak batu Anda? Untuk konteks, kami mencari untuk menerapkan tata letak batu hanya CSS untuk gambar di unsplash.com .
Oliver Joseph Ash
1
Sayangnya menggunakan JS untuk ini bukan pilihan. Kami ingin mengaktifkan rendering sisi server untuk alasan kinerja dan SEO. Ini berarti tata letak harus dirender sebelum JavaScript sisi-klien telah diunduh, diuraikan, dan dieksekusi. Mengingat ini tampaknya tidak mungkin, saya kira kita harus melakukan trade off di suatu tempat di sepanjang garis! Terima kasih atas bantuan Anda :-)
Oliver Joseph Ash
1
Pembaruan Spesifikasi: Dalam flexbox, margin dan padding persentase sekarang diatur untuk menyelesaikan kembali ukuran inline kontainer. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin
13

Baru-baru ini ditemukan teknik yang melibatkan flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

Artikel itu masuk akal bagi saya, tetapi saya belum mencoba menggunakannya, jadi saya tidak tahu apakah ada peringatan, selain yang disebutkan dalam jawaban Michael.

Berikut ini contoh dari artikel tersebut, yang memanfaatkan orderproperti tersebut, dikombinasikan dengan :nth-child.

Potongan tumpukan

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

Oliver Joseph Ash
sumber
Pertama, hanya tautan yang menjawab adalah yang buruk, seperti saat tautan tersebut mati, demikian pula jawabannya. Kedua, jika Anda membaca jawaban yang diberikan dengan lebih hati-hati, Anda akan menemukan apa yang disebutkan dalam tautan Anda tercakup. Sederhananya, ada terlalu banyak batasan menggunakan Flexbox saja, kecuali apa yang disebutkan di atas tidak menjadi masalah.
Ason
3
Saya telah membaca jawaban yang diterima dengan sangat teliti — Anda bahkan akan melihat beberapa komentar yang saya tambahkan di bagian bawahnya. Pendekatan flexbox yang saya tautkan unik dan tidak tercakup oleh jawaban itu. Saya tidak ingin mengulang artikel itu, karena ini sangat rumit, dan artikelnya sangat bagus untuk membahasnya.
Oliver Joseph Ash
Satu-satunya hal yang dibaut berbeda dari penggunaan orderproperti, membuatnya terlihat seperti barang mengalir dari kiri ke kanan. Namun, trik utamanya adalah flex-flow: column wrap, yang disebutkan dalam jawaban di atas. Selain itu, item tidak dapat mengalirkan barang seperti yang dilakukan pasangan bata asli, mis. Jika barang ke-3 dan ke-4 cocok di kolom ke-3, mereka akan, barang yang ditautkan tidak. Tetapi sekali lagi seperti yang saya katakan, itu semua tergantung pada apa persyaratannya, dan dalam hal ini (pertanyaan ini), itu tidak akan berfungsi seperti yang diminta.
Ason
Selain itu, ini bukan tentang "Anda tidak ingin mengulang artikel" , sebuah jawaban harus mengandung bagian penting dari kode, jadi itu masih akan valid ketika sumber daya yang ditautkan mati.
Ason
3
Saya memperbaruinya, menambahkan sampel dari artikel + upvote.
Ason