CSSのみの石積みレイアウト


134

私はかなり一般的な石積みレイアウトを実装する必要があります。ただし、いくつかの理由により、JavaScriptを使用したくありません。

高さが異なる長方形の複数列のグリッド。

パラメーター:

  • すべての要素の幅は同じです
  • 要素の高さがサーバー側では計算できない(画像とさまざまな量のテキスト)
  • 必要な場合は、固定数の列を使用できます

これには、最新のブラウザーであるプロパティで機能する簡単な解決策がありcolumn-countます。

そのソリューションの問題は、要素が列に順序付けられていることです。

左上のボックスから順に1から4の番号が付けられ、次の列の一番上のボックスは5のように続きます。

要素を行に並べる必要がありますが、少なくともおよそ:

左上のボックスから順に1から6の番号が付けられていますが、ボックス5は一番下のボックスが最短なので、左端の次のボックスよりも高い行にあるように見えるため、7です。

私が試したアプローチは機能しません:

これで、サーバー側のレンダリングを変更して、項目数を列数で割って項目を並べ替えることができますが、これは複雑でエラーが発生しやすい(ブラウザーが項目リストを列に分割する方法に基づいている)ので、可能であればそれを避けるために。

これを可能にするいくつかの新しいflexboxマジックがありますか?


7
定義済みの高さに依存しない方法は考えられません。JSを再考する場合は、stackoverflow.com / questions / 13518653 / …を参照してください。ここでは、非常にシンプルなソリューションを実装しています。
ガブリエレペトリオーリ2017年

1
@Gabyが今あなたのソリューションを実装しています、ありがとう!
ペッカ

4
CSSのみとおっしゃっていたようですね。MasonryはjQueryを必要としなくなりました-縮小されたライブラリーは8kb未満です-htmlだけで初期化できます。参考までにjsfiddle.net/wp7kuk1t
私は

1
要素の高さを事前に決定できる場合は、行の高さ、フォントサイズ(特定のフォントを提供し、いくつかの巧妙な計算を行う必要があります)、画像の高さ、垂直マージン、およびパディングを知ることで、次のことができます。これを行う。そうしないと、CSSだけを使用してこれを行うことはできません。PhantomJSのようなものを使用して各要素を事前にレンダリングし、その要素の高さを取得することもできますが、かなりのオーバーヘッド/待ち時間が追加されます。
ティモシーゾーン2017

回答:


157

フレックスボックス

動的な石積みのレイアウトは、少なくともクリーンで効率的な方法では、フレックスボックスでは不可能です。

Flexboxは1次元のレイアウトシステムです。これは、水平または垂直の線に沿って項目を整列できることを意味します。フレックスアイテムは、その行または列に限定されます。

真のグリッドシステムは2次元です。つまり、アイテムを水平線と垂直線に沿って配置できます。コンテンツアイテムは行と列に同時にまたがることができますが、フレックスアイテムではできません。

これが、フレックスボックスがグリッドを構築するための容量に制限がある理由です。また、W3Cが別のCSS3テクノロジーであるグリッドレイアウトを開発した理由でもあります。


row wrap

のフレックスコンテナではflex-flow: row wrap、フレックスアイテムは新しい行にラップする必要があります

つまり、フレックスアイテムは同じ行の別のアイテムの下にラップできません

上記のdiv#3div#1の下で折り返され、新しい行が作成されていることに注意してください。div#2の下ではラップできません。

その結果、アイテムが行の中で最も高くない場合、空白が残り、見苦しいギャップが生じます。


column wrap

に切り替えるとflex-flow: column wrap、グリッドのようなレイアウトが実現します。ただし、列方向のコンテナーには、すぐに4つの潜在的な問題があります。

  1. Flexアイテムは、水平ではなく垂直に流れます(この場合は必要です)。
  2. コンテナは(Pinterestレイアウトのように)垂直方向ではなく水平方向に拡張します。
  3. コンテナの高さが固定されている必要があるため、アイテムは折り返す場所を認識します。
  4. この記事の執筆時点では、追加の列に対応するためにコンテナが拡張されていないすべての主要なブラウザに欠陥があります

その結果、この場合、および他の多くの場合、列方向コンテナーはオプションではありません。


アイテムの寸法が未定義の CSSグリッド

コンテンツアイテムのさまざまな高さを事前に決定できる場合、グリッドレイアウトは問題の完璧な解決策になります。他のすべての要件は、グリッドの能力の範囲内です。

グリッドアイテムの幅と高さは、周囲のアイテムとのギャップを埋めるためにわかっている必要があります。

したがって、この場合、CSSが水平方向に流れる組積造レイアウトを構築するために提供する必要があるグリッドは最適ではありません。

実際、CSSテクノロジーが自動的にギャップを埋める機能を実現するまで、CSSには一般に解決策がありません。このようなものはおそらくドキュメントをリフローする必要があるので、それがどれほど役立つか効率的かはわかりません。

スクリプトが必要です。

JavaScriptソリューションは、絶対配置を使用する傾向があり、コンテンツアイテムをギャップなしで再配置するために、ドキュメントフローからコンテンツアイテムを削除します。次に2つの例を示します。


アイテムのディメンションが定義された CSSグリッド

コンテンツアイテムの幅と高さがわかっているレイアウトの場合、純粋な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-gapgrid-row-gap。このルールは、グリッドアイテム間に 10pxのギャップを設定します。(アイテムとコンテナーの間の領域には適用されません。)
  4. grid-template-columnsプロパティが明示的に定義された列の幅を設定します。

    repeat表記は、繰り返し列(または行)のパターンを定義します。

    このauto-fill関数は、コンテナーをオーバーフローさせずにできるだけ多くの列(または行)を整列するようグリッドに指示します。(これにより、フレックスレイアウトのと同様の動作が作成されますflex-wrap: wrap。)

    このminmax()関数は、各列(または行)の最小サイズと最大サイズの範囲を設定します。上記のコードでは、各列の幅はコンテナの最小30%であり、利用可能な空き領域の最大です。

    frユニットは、 Gridコンテナ内の空き領域の割合を表します。フレックスボックスのflex-growプロパティに匹敵します。

  5. grid-rowspanグリッドアイテムにまたがる行数を伝えます。


CSSグリッドのブラウザーサポート

  • Chrome-2017年3月8日現在、完全サポート(バージョン57)
  • Firefox-2017年3月6日時点で完全サポート(バージョン52)
  • Safari-2017年3月26日時点で完全にサポート(バージョン10.1)
  • Edge-2017年10月16日時点で完全サポート(バージョン16)
  • IE11-現在の仕様はサポートされていません。廃止されたバージョンをサポート

全体像は次のとおりです。http//caniuse.com/#search=grid


Firefoxのクールなグリッドオーバーレイ機能

Firefox開発ツールでは、グリッドコンテナーを検査すると、CSS宣言に小さなグリッドアイコンがあります。クリックすると、ページにグリッドのアウトラインが表示されます。

詳細はこちら:https : //developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts


5
素晴らしい答え!「アイテムの寸法が定義されたCSSグリッド」ソリューションを使用して、セルの高さをセル幅のパーセンテージとして表すことはできますか?これは、アスペクト比がわかっている画像を表示するのに役立ちます。常にアスペクト比を維持したい。
オリバージョセフアッシュ

ありがとう。画像のアスペクト比の問題、「セル幅のパーセンテージ」メソッド(パーセンテージのパディングトリック?)に関しては、グリッドまたはFlexboxでは信頼性がありません。こちらこちらをご覧ください。@OliverJosephAsh
マイケルベンジャミン

はい、私はパーセンテージパディングトリックを参照しています。石積みレイアウトソリューションと組み合わせて回避策を使用することは可能ですか?コンテキストについては、unsplash.comの画像に対してCSSのみの石積みレイアウトを実装することを検討しています。
オリバージョセフアッシュ

1
残念ながら、これにJSを使用することはできません。パフォーマンスとSEOの理由から、サーバー側のレンダリングを有効にしたいと考えています。つまり、クライアント側JavaScriptがダウンロード、解析、実行される前に、レイアウトをレンダリングする必要があります。これは不可能のように思われるので、私は線に沿ってどこかでトレードオフをする必要があると思います!ご協力ありがとうございます:-)
Oliver Joseph Ash

1
仕様の更新:フレックスボックスで、パーセンテージマージンとパディングが設定され、コンテナのインラインサイズが再度解決されるようになりました。drafts.c​​sswg.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
受け入れられた回答を非常によく読みました。下に追加したコメントも表示されます。私がリンクしたフレックスボックスのアプローチはユニークであり、その答えには含まれていません。記事は非常に複雑であるため、繰り返し説明したくありません。記事はそれをうまくカバーしています。
Oliver Joseph Ash

リンクが異なる唯一のことは、orderプロパティを利用することであり、アイテムが左から右に流れるように見せます。それでも、その主なトリックはflex-flow: column wrapであり、これは上記の回答で言及されています。さらに、実際の石積みのようにアイテムを流すことはできません。たとえば、3番目と4番目のアイテムが3番目の列に収まる場合、リンクされたアイテムは収まりません。しかし、もう一度言ったように、それはすべて要件が何であるかに依存し、この場合(この質問)、要求されたとおりに機能しません。
エイソン

さらに、それは「記事を繰り返したくない」ということではありません。回答にはコードの本質的な部分が含まれている必要があります。そのため、リンクされたリソースが停止しても有効になります。
エイソン

3
更新し、記事のサンプルと賛成票を追加しました。
エイソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.