【js】メイソンリーレイアウトの簡単な実装 | KonNotes

【js】メイソンリーレイアウトの簡単な実装

Masonry css
csshtmljsWEB制作

Masonry Layout(メイソンリーレイアウト)とは、Web制作上の文脈ではPinterestに代表されるタイル上のグリッドレイアウトのことです。

あるアーティストのサイトで、作品をメイソンリーレイアウトで表示したいという依頼がありました。

アーティスト作品という性質上その画像のトリミングはしたくなく、また提供される画像の高さもバラバラなため、単純に横並びにすると空白が目立ってしまいます。

これまでは実現するにはjQueryを利用したMasonry.jsなどライブラリを使うことが一般的でしたが、jsで簡単にできないかなと思いこの記事に次回実装する際の備忘録として残しておこうと思います。

ちなみに、「Masonry」は語源は「mason(石工、大工)」に由来し、建築やレイアウトに関する意味があるらしいです。

CodePen

html

<div class="l-masonry">
  <div class="l-masonry__list js-masonry">
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/jakub-dziubak-XtUd5SiX464-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/clay-banks-_wkd7XBRfU4-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/fahmi-fakhrudin-nzyzAUsbV0M-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/kelly-sikkema-IhxM7w392e0-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/brigitte-tohm-EAay7Aj4jbc-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/nathan-dumlao-6VhPY27jdps-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/emre-NZMeJsrMC8U-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/nathan-dumlao-pMW4jzELQCw-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/nathan-dumlao-Ic8RmXGyfNc-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/pawel-czerwinski-_NPyWow9CZk-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/christina-rumpf-LMzwJDu6hTE-unsplash.jpg" alt="Sample Image" />
    </div>
    <div class="l-masonry__item">
      <img src="https://www.konnotes.com/cms/wp-content/uploads/2025/05/trent-erwin-fKImVjUwkXc-unsplash.jpg" alt="Sample Image" />
    </div>
  </div>
</div>

実際はWordPressにて投稿されるので、.l-masonry__itemの増減が発生する想定です。

css(scss)

.l-masonry {
  &__list {
    display: flex;
    gap: 8px;
  }
  &__column {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  &__item {
    width: 100%;
    img {
      width: 100%;
      max-width: 100%;
      height: auto;
    }
  }
}

cssはgridを使わず、flexboxにて実装しています。

js

document.addEventListener("DOMContentLoaded", () => {
  const masonryList = document.querySelector('.js-masonry');
  const masonryItems = Array.from(document.querySelectorAll('.js-masonry .l-masonry__item'));
  const columnCount = 4; // 列数
  const columns = Array.from({ length: columnCount }, () => []);

  masonryItems.forEach((item, index) => {
    const columnIndex = index % columnCount;
    columns[columnIndex].push(item);
  });

  columns.forEach((column) => {
    const columnWrapper = document.createElement('div');
    columnWrapper.classList.add('l-masonry__column');
    column.forEach((item) => columnWrapper.appendChild(item));
    masonryList.appendChild(columnWrapper);
  });  
});

.l-masonry__itemを4列に分割して、cssにて縦積みしているだけです。

const columnCount = 4;

この数値を変更すれば任意の列に変更できます。

問題点

この実装方法だと、各列の画像の合計の高さが揃えば、すべての列の下辺は綺麗に揃います。

ですが、入ってくる画像の順番によっては特定の列が長くなり過ぎてしまったり、短くなったりします。

画像のアスペクト比が違いすぎるとその差は顕著になり、すべての列の下辺がかなりでこぼこになります。

WordPressで運用するのである程度カバーや調整はできそうですが限界はあるため

「順番は考慮せずに各列の高さが最小になるようにjsで計算し並び替える」

みたいな処理も、場合によっては必要かもしれません。

まとめ

今回は画像をトリミングしないという制約上、メイソンリーレイアウトにトライしてみました。

ただ、もう少しいい方法がありそうな気もするので、良い方法を見つけたら追記しようと思います。