

画像を横3列にして、画面の幅に応じて列数を変えたいです。

■やりたいこと  
・画像を3列表示にして、画面の幅が狭くなると3列から2列になるような感じにしたいです。  
・コードでは画像の順番は固定になっていますが、動的にする予定なので、画像の大きさは入れ変わるので、画像が入れ替わっても隙間なくいい感じにしたいです。  
→”いい感じ”とは言語化が難しいので、添付画像を載せました。

3列にしても画像の大きさがバラバラなので隙間できてしまい、隙間が詰めれない所と幅に応じて列数を変えることがどうやっても上手くいかなく、、、  
分かる方教えて頂きたいです。よろしくお願いします。

[code]

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset='utf-8'>
    <style>
    
    .flex{
      padding: 2.5% 0;
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
    }
    .flex > *{
      min-width: 8%;
      flex-basis: 25%;
      background: #fff;
      font-size: 64px;
      text-align: center;
    }
    .flex,
    .flex > *{
      border: 3px solid #aaa;
    }
    
    </style>
    </head>
    <body>
    <div class="flex">
    
    <img src="https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg" width="180" height="120">
    <img src="https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg" width="180" height="100">
    <img src="https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg" width="180" height="320">
    <img src="https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg" width="180" height="100">
    <img src="https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg" width="180" height="100">
    <img src="#" width="180" height="320">
    <img src="#" width="180" height="140">
    <img src="#" width="180" height="170">
    <img src="#" width="180" height="120">
    
    </div>
    </body>
    </html>
[/code]

  
[![画像の説明をここに入力](https://i.stack.imgur.com/4jodo.png)](https://i.stack.imgur.com/4jodo.png)


CC BY-SA 4.0
2022-01-28T16:59:16.833
85991
2022-02-05T09:47:43.767
2022-02-03T13:03:13.347
50569
50569
question
javascript html css
画像を横3列にして、画面の幅に応じて列数を変えたいです。


[Flexbox](https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Flexbox)
が使えるかもです。  
縦に並べ, 折り返すことで 3列に。

その際, (横幅ではなく)一覧のボックスの高さにより折り返し, 次の列に続きます。  
(`height` 変更するとそれに合わせ変化する)

[code]

    .content {
        width: 200px;
        height: 230px;
        border: 1px solid #c3c3c3;
        display: flex;
        flex-wrap: wrap;
    }
    
    #col {
        flex-direction: column;
    }
    
    .box {
        width: 50px;
    }
    
    .red {
        background-color: red;
        height: 50px;
    }
    
    .lightblue {
        background-color: lightblue;
        height: 70px;
    }
    
    .yellow {
        background-color: yellow;
        height: 50px;
    }
[/code]

[code]

      <section>
        <article>
          <div id="col" class="content">
            <div class="box red">A</div>
            <div class="box lightblue">B</div>
            <div class="box yellow">C</div>
            <div class="box red">D</div>
            <div class="box lightblue">E</div>
            <div class="box yellow">F</div>
            <div class="box red">G</div>
            <div class="box lightblue">H</div>
            <div class="box yellow">I</div>
          </div>
        </article>
      </section>
[/code]

なので 3列に並べる場合, 次のような高さを求める処理が必要かも

  1. 各画像の高さを求め配列か何かにする。合計値が全体の長さ。
  2. 長さ 1/3 に相当する項目の位置を見つけ, その項目も含めた値を 1/3 相当の(仮の)長さとする
  3. その次の項目から 2列目。(2)で求めた長さで切り捨てた結果が 4列になりそうなら, 仮の長さ(2)を拡張(再計算)
  4. これにより, 一覧ボックスの高さが決まる

画像一覧取得時に 全体の長さも取得できているなら, `calc` で 1/3 + α の長さで適当に決め打ちするのも, 場合によってはありかも。

あと他には, 幅が狭いかどうかの判断を JavaScriptで行い,  
一覧ボックスの高さを 1/2 相当の長さとして算出を行えば 2列に表示可能

参考: (developer.mozilla.org)

  * [Flexbox](https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Flexbox)
  * [フレックスアイテムの折り返しのマスター](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Mastering_Wrapping_of_Flex_Items)

* * *

#### 追記 (Google Colabで動作確認可能)

Google Colab あるいは Jupyter で動作確認できるコード用意してみたです

[code]

    from ipywidgets import Layout, Button, Box, VBox
    from random import randint
    box_layout = Layout(display='flex',
                        flex_flow='column wrap',
                        width='40%', height='400px',
                        border='thin solid blue')
    
    arr = [randint(30, 200) for n in range(10)]
    # arr[n] `-4` の数値はたぶん Buttonの marginか何か (普通は無視してよいかも)
    items = [Button(description=f'{n}', layout=Layout(width='120px', height=f'{arr[n] -4}px'), button_style='warning')
             for n in range(len(arr))]
    display(VBox(children=items, layout=box_layout))
    
    def fn(arr, ncol):
        height, mod = divmod(sum(arr), ncol)
        if mod: height += 1
        s,it_arr = 0,iter(arr)
        for n in it_arr:
            s += n
            if s < height: continue
            ncol -= 1
            if ncol == 1:
                return s if n +sum(it_arr) > height else height
            height,s = s,0
    
    # `+3` の数値は, padか marginか何か … 普通は(htmlでは)無視してよいかも
    box_layout.height = f'{fn(arr, 3) +3}px'
    
[/code]

* * *

#### 追記 (上から順に配置する場合) -- コメント受けて

高さの異なる項目を, 上から順に詰めて並べていく場合

Layoutとしては, @hinaloe さんの回答にあるように組積レイアウト(Masonry_Layout) の登場・普及まで待つ必要があるでしょう

現状は, HTML内の要素をそのまま並べることはできないため, 一旦 JavaScriptで取り込み再配置の必要があるでしょう

  * 配列の形で要素を持っておき, 表示の段階で並べる (配列は `fetch` など利用しサーバー側から取得するなど)
  * HTML内の要素を取り扱うのなら, 要素を一旦 配列として取り込む

再配置は JavaScriptで行うのなら, 以下のような方法

  * top, left などの属性指定し, 座標を固定する方法
  * 3つ(あるいは 2つ)の Flexboxを用意し横に並べる。ひとつの Flexbox内では縦に並べる

最初は, それぞれの列の高さを 0 とし, 項目追加しながらそれぞれの列の高さを更新  
追加する列の求め方は, 高さの一番低い列。それが複数あるなら左側の列。  
(あるいは望むような独自の方法)

* * *

(素の)JavaScript以外なら, Vue や React を使う方法になるかも

参考:

  * [Understanding client-side JavaScript frameworks](https://developer.mozilla.org/ja/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks)

  * [Reactをはじめる](https://developer.mozilla.org/ja/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started)

  * (Vue.js 版) [vue-masonry](https://github.com/shershen08/vue-masonry)


CC BY-SA 4.0
2022-01-29T11:35:58.270
86004
2022-02-05T09:47:43.767
2022-02-05T09:47:43.767
43025
43025
85991
answer


将来的には[masonry
layout](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout)を利用することで`grid`で簡単にこれが行えるようになることが期待されますが，残念ながら[現状はFirefoxでしか](https://caniuse.com/?search=masonry)，それも実験フラグの設定をした上でしか利用できないようです．

[code]

    ul {
      list-style: none;
    }
    
    li {
      background: #ccc;
    }
    
    
    .grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-template-rows: masonry;
      grid-auto-flow: row;
      gap: 20px;
    }
    
    .grid img {
      width: 100%;
    /*   height: auto; */
    }
[/code]

[code]

    <div class="test">
        <ul class="grid">
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="120"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="100"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="320"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="100"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="100"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="320"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="140"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="170"></li>
            <li><img src=https://www.pakutaso.com/shared/img/thumb/penfanresIMGP4683_TP_V.jpg width="180" height="120"></li>
        </ul>
    </div>
[/code]

なので現時点ではoririさんの回答にあるようなflexboxを利用することになるでしょうか……．


CC BY-SA 4.0
2022-01-31T02:07:44.560
86027
2022-01-31T02:07:44.560
2376
85991
answer