Bố cục nề chỉ CSS


134

Tôi cần phải thực hiện một bố cục xây dựng khá bình thường. Tuy nhiên, vì một số lý do tôi không muốn sử dụng JavaScript để làm điều đó.

Một lưới gồm nhiều cột hình chữ nhật có chiều cao khác nhau.

Thông số:

  • Tất cả các yếu tố có cùng chiều rộng
  • Các phần tử có chiều cao không thể tính được phía máy chủ (một hình ảnh cộng với số lượng văn bản khác nhau)
  • Tôi có thể sống với một số cột cố định nếu tôi phải

có một giải pháp tầm thường cho việc này hoạt động trong các trình duyệt hiện đại, thuộc column-counttính.

Vấn đề với giải pháp đó là các phần tử được sắp xếp theo cột:

Bắt đầu từ hộp trên cùng bên trái, chúng được đánh số từ 1 đến 4 thẳng xuống, hộp trên cùng trong cột tiếp theo là 5, v.v.

Mặc dù tôi cần các phần tử được sắp xếp theo hàng, ít nhất là khoảng:

Bắt đầu từ hộp ngoài cùng bên trái, chúng được đánh số từ 1 đến 6 thẳng, nhưng vì hộp 5 là hộp ngắn nhất bên dưới nó là 7 vì nó có vẻ như nằm trên một hàng cao hơn hộp tiếp theo ở bên trái.

Phương pháp tôi đã thử mà không hiệu quả:

Bây giờ tôi có thể thay đổi kết xuất phía máy chủ và sắp xếp lại các mục chia số lượng mục cho số cột, nhưng điều đó phức tạp, dễ bị lỗi (dựa trên cách trình duyệt quyết định chia danh sách mục thành cột), vì vậy tôi muốn để tránh nó nếu có thể.

Có một số ma thuật flexbox mới lạ làm cho điều này có thể?


7
Không thể nghĩ ra một cách không phụ thuộc vào độ cao được xác định trước. Nếu bạn xem xét lại JS, hãy xem stackoverflow.com/questions/13518653/õ nơi tôi thực hiện một giải pháp như vậy khá đơn giản.
Gabriele Petrioli

1
@Gaby thực hiện giải pháp của bạn ngay bây giờ, cảm ơn bạn!
Pekka

4
Tôi nhận ra rằng bạn chỉ nói CSS. Tôi chỉ muốn đề cập rằng Masonry không còn yêu cầu jQuery - thư viện rút gọn dưới 8kb - và có thể được khởi tạo chỉ bằng html. Chỉ để tham khảo jsfiddle.net/wp7kuk1t
Tôi haz

1
Nếu bạn có thể xác định chiều cao của các yếu tố trước thời hạn, bằng cách biết chiều cao dòng, kích thước phông chữ (bạn phải phục vụ một phông chữ cụ thể và thực hiện một số tính toán thông minh), chiều cao hình ảnh, lề và đệm, bạn có thể làm cái này. Nếu không, bạn không thể làm điều này bằng cách chỉ sử dụng CSS. Bạn cũng có thể sử dụng một cái gì đó như PhantomJS để kết xuất trước từng phần tử và lấy chiều cao của phần tử đó, nhưng sẽ có thêm chi phí / độ trễ đáng kể.
Timothy Zorn

Câu trả lời:


157

Hộp linh hoạt

Không thể bố trí khối xây bằng động với flexbox, ít nhất là không theo cách sạch sẽ và hiệu quả.

Flexbox là một hệ thống bố trí một chiều. Điều này có nghĩa là nó có thể sắp xếp các mục dọc theo các đường thẳng đứng HOẶC dọc. Một mục flex được giới hạn trong hàng hoặc cột của nó.

Một hệ thống lưới thực sự là hai chiều, có nghĩa là nó có thể sắp xếp các mục dọc theo các đường ngang VÀ dọc. Các mục nội dung có thể trải dài trên các hàng và cột đồng thời, điều mà các mục flex không thể làm được.

Đây là lý do tại sao flexbox có khả năng hạn chế để xây dựng lưới điện. Đó cũng là một lý do tại sao W3C đã phát triển một công nghệ CSS3 khác, Grid Layout .


row wrap

Trong một hộp chứa flex flex-flow: row wrap, các vật phẩm flex phải bọc thành các hàng mới .

Điều này có nghĩa là một mặt hàng flex không thể bọc dưới một mặt hàng khác trong cùng một hàng .

Lưu ý ở trên cách div # 3 kết thúc bên dưới div # 1 , tạo một hàng mới. Nó không thể bao bọc bên dưới div # 2 .

Kết quả là, khi các vật phẩm không phải là cao nhất trong hàng, khoảng trắng vẫn còn, tạo ra những khoảng trống khó coi.


column wrap

Nếu bạn chuyển sang flex-flow: column wrap , một bố cục giống như lưới sẽ đạt được nhiều hơn. Tuy nhiên, một container hướng cột có bốn vấn đề tiềm ẩn ngay lập tức:

  1. Các mặt hàng Flex chảy theo chiều dọc, không theo chiều ngang (như bạn cần trong trường hợp này).
  2. Container mở rộng theo chiều ngang, không theo chiều dọc (như bố cục Pinterest).
  3. Nó đòi hỏi các container phải có một chiều cao cố định, vì vậy các mặt hàng biết nơi để bọc.
  4. Theo văn bản này, nó có một thiếu sót trong tất cả các trình duyệt chính nơi container không mở rộng để chứa các cột bổ sung .

Kết quả là, một thùng chứa hướng cột không phải là một lựa chọn trong trường hợp này và trong nhiều trường hợp khác.


Lưới CSS với kích thước mục không xác định

Bố cục lưới sẽ là một giải pháp hoàn hảo cho vấn đề của bạn nếu độ cao khác nhau của các mục nội dung có thể được xác định trước . Tất cả các yêu cầu khác đều nằm trong khả năng của Grid.

Phải biết chiều rộng và chiều cao của các mục lưới để thu hẹp khoảng cách với các mục xung quanh.

Vì vậy, Grid, CSS tốt nhất phải cung cấp để xây dựng bố cục khối xây theo chiều ngang, bị thiếu trong trường hợp này.

Trong thực tế, cho đến khi một công nghệ CSS xuất hiện với khả năng tự động đóng các khoảng trống, CSS nói chung không có giải pháp. Một cái gì đó như thế này có thể sẽ yêu cầu chỉnh lại tài liệu, vì vậy tôi không chắc nó sẽ hữu ích và hiệu quả như thế nào.

Bạn sẽ cần một kịch bản.

Các giải pháp JavaScript có xu hướng sử dụng định vị tuyệt đối, loại bỏ các mục nội dung khỏi luồng tài liệu để sắp xếp lại chúng không có khoảng trống. Đây là hai ví dụ:


Lưới CSS với kích thước mục được xác định

Đối với các bố cục nơi chiều rộng và chiều cao của các mục nội dung được biết đến, đây là cách bố trí khối xây theo chiều ngang trong CSS thuần túy:

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>

bản demo jsFiddle


Làm thế nào nó hoạt động

  1. Thiết lập một container lưới cấp khối. ( inline-gridsẽ là lựa chọn khác)
  2. Các grid-auto-rowsbất động sản thiết lập chiều cao của hàng tự động tạo ra. Trong lưới này, mỗi hàng cao 50px.
  3. Tài grid-gapsản là một tốc ký cho grid-column-gapgrid-row-gap. Quy tắc này đặt khoảng cách 10px giữa các mục lưới. (Nó không áp dụng cho khu vực giữa các mặt hàng và container.)
  4. Các grid-template-columnsbất động sản thiết lập chiều rộng của cột được xác định một cách rõ ràng.

    Các repeatký hiệu định nghĩa một mô hình của các cột lặp lại (hoặc hàng).

    Các auto-fillchức năng bảo lưới điện để dòng lên như nhiều cột (hoặc hàng) càng tốt mà không tràn container. (Điều này có thể tạo ra một hành vi tương tự như bố trí flexflex-wrap: wrap .)

    Các minmax()chức năng thiết lập một phạm vi kích thước tối thiểu và tối đa cho mỗi cột (hoặc hàng). Trong mã ở trên, chiều rộng của mỗi cột sẽ tối thiểu là 30% của container và tối đa của bất kỳ không gian trống nào có sẵn.

    Các frđơn vị đại diện cho một phần nhỏ của không gian trống trong container lưới. Nó tương đương với flex-growtài sản của flexbox .

  5. Với grid-rowspanchúng tôi đang nói với các mục lưới có bao nhiêu hàng chúng nên trải dài.


Hỗ trợ trình duyệt cho CSS Grid

  • Chrome - hỗ trợ đầy đủ kể từ ngày 8 tháng 3 năm 2017 (phiên bản 57)
  • Firefox - hỗ trợ đầy đủ kể từ ngày 6 tháng 3 năm 2017 (phiên bản 52)
  • Safari - hỗ trợ đầy đủ kể từ ngày 26 tháng 3 năm 2017 (phiên bản 10.1)
  • Edge - hỗ trợ đầy đủ kể từ ngày 16 tháng 10 năm 2017 (phiên bản 16)
  • IE11 - không hỗ trợ cho thông số kỹ thuật hiện tại; hỗ trợ phiên bản lỗi thời

Đây là bức tranh hoàn chỉnh: http://caniuse.com/#search=grid


Tính năng lớp phủ lưới tuyệt vời trong Firefox

Trong các công cụ dev của Firefox, khi bạn kiểm tra bộ chứa lưới, có một biểu tượng lưới nhỏ trong khai báo CSS. Khi nhấp vào, nó sẽ hiển thị một phác thảo về lưới của bạn trên trang.

Thêm chi tiết tại đây: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts


5
Câu trả lời tuyệt vời! Với giải pháp "Lưới CSS với kích thước mục được xác định", có thể biểu thị chiều cao của ô theo phần trăm chiều rộng của ô không? Điều này sẽ hữu ích để hiển thị hình ảnh, trong đó tỷ lệ khung hình được biết đến. Chúng tôi muốn duy trì tỷ lệ khung hình mọi lúc.
Oliver Joseph Ash

Cảm ơn. Đối với vấn đề tỷ lệ khung hình cho hình ảnh, phương pháp "tỷ lệ phần trăm chiều rộng ô" (thủ thuật đệm phần trăm?), Không đáng tin cậy trong Grid hoặc Flexbox, đối với vấn đề đó. Xem ở đâyở đây . @OliverJosephAsh
Michael Benjamin

Vâng, tôi đang đề cập đến thủ thuật phần trăm đệm. Có thể sử dụng bất kỳ cách giải quyết nào kết hợp với giải pháp bố trí khối xây của bạn không? Đối với ngữ cảnh, chúng tôi đang tìm cách triển khai bố cục nề chỉ CSS cho các hình ảnh trên unsplash.com .
Oliver Joseph Ash

1
Thật không may, sử dụng JS cho điều này không phải là một lựa chọn. Chúng tôi muốn kích hoạt kết xuất phía máy chủ vì lý do hiệu suất và SEO. Điều này có nghĩa là bố cục phải được hiển thị trước khi JavaScript phía máy khách đã tải xuống, phân tích cú pháp và thực thi. Vì điều này dường như là không thể, tôi đoán chúng ta sẽ phải đánh đổi ở đâu đó dọc theo đường! Cảm ơn sự giúp đỡ của bạn :-)
Oliver Joseph Ash

1
Cập nhật thông số: Trong flexbox, tỷ lệ phần trăm và phần đệm hiện được đặt để giải quyết lại kích thước nội tuyến của bộ chứa. dự thảo.csswg.org/ css
Michael Benjamin

13

Đây là kỹ thuật được phát hiện gần đây liên quan đến flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

Bài viết có ý nghĩa với tôi, nhưng tôi đã không thử sử dụng nó, vì vậy tôi không biết liệu có bất kỳ cảnh báo nào, ngoài những câu được đề cập trong câu trả lời của Michael không.

Đây là một mẫu từ bài viết, sử dụng ordertài sản, kết hợp với :nth-child.

Đoạn trích

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


Trước hết, liên kết chỉ có câu trả lời là xấu, vì khi liên kết đó chết, câu trả lời cũng vậy. Thứ hai, nếu bạn đọc câu trả lời cẩn thận hơn, bạn sẽ thấy những gì được đề cập trong liên kết của bạn được đề cập. Nói một cách đơn giản, có quá nhiều hạn chế khi sử dụng Flexbox, trừ khi những gì được đề cập ở trên không phải là vấn đề.
Ason

3
Tôi đã đọc câu trả lời được chấp nhận rất kỹ, bạn thậm chí sẽ thấy một số bình luận tôi đã thêm vào phần dưới của nó. Cách tiếp cận flexbox tôi liên kết đến là duy nhất và không nằm trong câu trả lời đó. Tôi không muốn lặp lại bài viết, bởi vì nó rất phức tạp và bài viết thực hiện rất tốt việc bao quát điều đó.
Oliver Joseph Ash

Điều duy nhất mà liên kết thực hiện khác nhau là sử dụng ordertài sản, làm cho nó trông giống như các vật phẩm chảy từ trái sang phải. Tuy nhiên, mẹo chính của nó là flex-flow: column wrap, được đề cập trong câu trả lời ở trên. Ngoài ra, nó không thể chuyển các mục theo cách mà một thợ nề thực sự làm, ví dụ: nếu mục thứ 3 và thứ 4 phù hợp với cột thứ 3, thì họ sẽ không liên kết. Nhưng một lần nữa như tôi đã nói, tất cả phụ thuộc vào yêu cầu là gì, và trong trường hợp này (câu hỏi này), nó sẽ không hoạt động như những gì được yêu cầu.
Ason

Hơn nữa, đây không phải là "bạn không muốn lặp lại bài viết" , một câu trả lời nên chứa phần thiết yếu của mã, vì vậy nó sẽ vẫn có hiệu lực khi tài nguyên được liên kết chết.
Ason

3
Tôi đã cập nhật nó, thêm một mẫu từ bài viết + một upvote.
Ason
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.