横並び時に特定要素の高さを揃えたい場合の実装(備忘録)

今回は、横並び時に特定の要素の高さを揃えたい場合の実装について、CSSだけで対応する方法、JSを使って対応する方法を紹介していきます。

具体的には、下記になります。

  • CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)
  • CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)
  • JSで横並びで折り返すカラムの行に応じて高さを揃える方法
  • JSで横並びで折り返さないカラムで高さを揃える方法

それでは実装に入っていきますが、最初にすべてのコードを紹介してから詳細に入っていきたいと思います。

HTMLの記述について

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>横並びのときに要素の高さを揃える</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <!-- CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素) -->
  <section>
    <div class="ly_inner">
      <h2>CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)</h2>
      <div class="bl_flex">
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </section>
  <!-- CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素) -->
  <section>
    <div class="ly_inner">
      <h2>CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)</h2>
      <p>見出しとボタンの外側にタグを増やしています。</p>
      <div class="bl_flex">
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <div class="bl_content_inner">
            <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
            <a href="">ボタン</a>
          </div>
        </div>
      </div>
    </div>
  </section>
  <!-- CSSで横並びで高さを厳密には揃えられない場合 -->
  <section>
    <div class="ly_inner">
      <h2>CSSで横並びで高さを厳密には揃えられない場合</h2>
      <p>以下のように見出し、テキストなど複数の要素が入る場合は基本的にできません。<br>gridのsubgridを使える時代がやってきた時に紹介したいと思います。</p>
      <div class="bl_flex">
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
          <p>テキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し</h3>
          <p>テキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_flex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </section>
  <!-- JSで横並びで折り返すカラムの行に応じて高さを揃える場合 -->
  <section>
    <div class="ly_inner">
      <h2>JSで横並びで折り返すカラムの行に応じて高さを揃える方法</h2>
      <p>レスポンシブ対応しています。見出し、テキスト、ボタンの外側にタグを挿入しても使用可能です。今回は入れていません。</p>
      <div class="bl_jsflex">
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text">テキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し見出し見出し</h3>
          <p class="el_text">テキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text">テキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title">見出し見出し</h3>
          <p class="el_text">テキストが入ります</p>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </section>
  <!-- JSで横並びで折り返さないカラムで高さを揃える場合 -->
  <section>
    <div class="ly_inner">
      <h2>JSで横並びで折り返さないカラムで高さを揃える方法</h2>
      <p>スライダーやカルーセルの場合でも使えます。</p>
      <div class="bl_jsflex02">
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </section>
  <script src="style.js"></script>
</body>
</html>

CSSの記述について

@charset "utf-8";

/* base */
*:focus-visible {
  outline: -webkit-focus-ring-color auto 1px;
}
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  display: block;
  width: 100%;
  height: auto;
  margin-bottom: 8px;
}

h2 {
  font-size: 32px;
  margin-bottom: 24px;
  font-weight: bold;
}
.ly_inner {
  padding: 80px 0;
  width: min(1120px, 100%);
  margin-inline: auto;
}
.bl_flex {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}
a {
  display: grid;
  place-items: center;
  border: 1px solid #000;
  min-height: 40px;
}
p {
  margin-bottom: 16px;
}

/* CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)
------------------------------------------------------ */
.bl_flex_item {
  width: calc((100% - 48px) / 3);
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column; /* 縦に並べるために指定 */
  background-color: #ccc;
}
.el_h3title {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
  flex-grow: 1; /* 子要素にflexを指定したさらに子要素にflex-growを指定する */
}
/* CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)
------------------------------------------------------ */
/* 方法1の見出しとボタンの外側にデータが入る場合 */
.bl_content_inner {
  padding: 16px;
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column;
  flex-grow: 1;
}

/* ここからJS用
------------------------------------------------------ */
.bl_jsflex {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}

.bl_jsflex_item {
  width: calc((100% - 48px) / 3);
  background-color: #ccc;
}
.el_jsh3title {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
}
.el_text {
  font-size: 16px;
}
@media screen and (max-width: 767px) {
  .bl_jsflex_item {
    width: calc((100% - 24px) / 2);
  }
}

.bl_jsflex02 {
  display: flex;
  gap: 24px;
}
.bl_jsflex_item02 {
  width: calc((100% - 48px) / 3);
  background-color: #ccc;
}
.el_jsh3title02 {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
}
.el_text02 {
  font-size: 16px;
}

JavaScriptの記述について

"use strict";

// JSで横並びで折り返すカラムの行に応じて高さを揃える場合
function resizeHandler() {
  const flexItems = document.querySelectorAll(".bl_jsflex_item");
  let itemsPerRow = 3; // デフォルトの1行あたりの要素数

  if (window.innerWidth <= 767) {
    itemsPerRow = 2; // 767px以下の場合、カラム数を2に設定
  }

  let currentRowItems = [];
  let currentRow = 0;

  flexItems.forEach((item, index) => {
    item.style.height = ""; // 元の高さをリセット

    currentRowItems.push(item);

    if ((index + 1) % itemsPerRow === 0 || index === flexItems.length - 1) {
      adjustHeight(currentRowItems);
      currentRowItems = [];
      currentRow++;
    }
  });
}

function adjustHeight(targets) {
  let maxTitleHeight = 0;
  let maxTextHeight = 0;

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = ""; // タイトルの高さをリセット
    text.style.height = ""; // テキストの高さをリセット

    maxTitleHeight = Math.max(maxTitleHeight, title.clientHeight);
    maxTextHeight = Math.max(maxTextHeight, text.clientHeight);
  });

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = maxTitleHeight + "px";
    text.style.height = maxTextHeight + "px";
  });
}

window.addEventListener("resize", resizeHandler);
window.addEventListener("load", resizeHandler);

// JSで横並びで折り返さないカラムで高さを揃える場合
function resizeHandler02() {
  const title = document.querySelectorAll(".el_jsh3title02");
  const text = document.querySelectorAll(".el_text02");

  let maxTitleHeight = 0;
  let maxTextHeight = 0;

  title.forEach((target) => {
    target.style.height = "";
    const height = target.clientHeight;
    maxTitleHeight = Math.max(maxTitleHeight, height);
  });

  text.forEach((target) => {
    target.style.height = "";
    const height = target.clientHeight;
    maxTextHeight = Math.max(maxTextHeight, height);
  });

  title.forEach((target) => {
    target.style.height = maxTitleHeight + "px";
  });

  text.forEach((target) => {
    target.style.height = maxTextHeight + "px";
  });
}

window.addEventListener("resize", resizeHandler02);
window.addEventListener("load", resizeHandler02);

以上がすべてのコードになります。これらをもとに、4つの方法を順番に解説していきます。

CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)

HTML詳細

<!-- CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素) -->
<section>
  <div class="ly_inner">
    <h2>CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)</h2>
    <div class="bl_flex">
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
        <a href="">ボタン</a>
      </div>
    </div>
  </div>
</section>

こちらは見ていただくとわかると思いますが、下記の構造になっております。

bl_flex(親)
┗bl_flex_item(子)
 ┗img(孫)
 ┗h3(孫)
 ┗a(孫)

上記はbl_flexという親からみて、bl_flex_itemが子の関係、img・h3・aが孫の関係になります。

今回は孫である、h3のテキストの高さを横に並べた時に合わせていきます。

CSS詳細

.bl_flex {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}
a {
  display: grid;
  place-items: center;
  border: 1px solid #000;
  min-height: 40px;
}
p {
  margin-bottom: 16px;
}
.bl_flex_item {
  width: calc((100% - 48px) / 3);
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column; /* 縦に並べるために指定 */
  background-color: #ccc;
}
.el_h3title {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
  flex-grow: 1; /* 子要素にflexを指定したさらに子要素にflex-growを指定する */
}

ポイントはbl_flex_itemです。ここに対してdisplayをflexで指定し、横に並ばないようflex-directionをcolumnにします。これはのちのh3に対してflex-growを指定するために必要になります。

続いてel_h3titleにflex-growを1と指定すれば完了です。

ざっくりいうと、親にも子にもflexを当てて、孫にflex-growという感じですかね。
続いてはひ孫要素があるパターンもみていきましょう。

CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)

HTML詳細

<!-- JSで横並びで折り返さないカラムで高さを揃える場合 -->
  <section>
    <div class="ly_inner">
      <h2>JSで横並びで折り返さないカラムで高さを揃える方法</h2>
      <p>スライダーやカルーセルの場合でも使えます。</p>
      <div class="bl_jsflex02">
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
        <div class="bl_jsflex_item02">
          <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
          <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <p class="el_text02">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </section>
  <script src="style.js"></script>
  <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-7420886115642360" data-ad-slot="6763072797" data-ad-format="auto" data-full-width-responsive="true"></ins>
  <script>
    (adsbygoogle = window.adsbygoogle || []).push({});
  </script>
</body>

</html>
<!-- CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素) -->
<section>
  <div class="ly_inner">
    <h2>CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)</h2>
    <p>見出しとボタンの外側にタグを増やしています。</p>
    <div class="bl_flex">
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
      <div class="bl_flex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <div class="bl_content_inner">
          <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
          <a href="">ボタン</a>
        </div>
      </div>
    </div>
  </div>
</section>

前回との違いは、下記にbl_content_innerが追加され、さらにひ孫要素まで現れることになった点です。

<div class="bl_content_inner">
  <h3 class="el_h3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
  <a href="">ボタン</a>
</div>

先程と同じように、階層を表すと下記のようになります。

bl_flex(親)
┗bl_flex_item(子)
 ┗img(孫)
 ┗bl_content_inner(孫)
  ┗h3(ひ孫)
  ┗a(ひ孫)

今回は、ひ孫であるh3の高さを横に並んだ時に揃うようにしていきます。

CSS詳細

bl_flex {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}
a {
  display: grid;
  place-items: center;
  border: 1px solid #000;
  min-height: 40px;
}
p {
  margin-bottom: 16px;
}
.bl_flex_item {
  width: calc((100% - 48px) / 3);
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column; /* 縦に並べるために指定 */
  background-color: #ccc;
}
.el_h3title {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
  flex-grow: 1; /* 子要素にflexを指定したさらに子要素にflex-growを指定する */
}
/* CSSで横並びで高さを揃える方法2(親要素・子要素・孫要素・ひ孫要素)
------------------------------------------------------ */
/* 方法1の見出しとボタンの外側にデータが入る場合 */
.bl_content_inner {
  padding: 16px;
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column;
  flex-grow: 1;
}

ポイントは下記です。
先ほどの「CSSで横並びで高さを揃える方法1(親要素・子要素・孫要素)」のコードに下記を追加するだけで、実現できます。

.bl_content_inner {
  padding: 16px;
  display: flex; /* 親要素にflexを使用した子要素に対してもflexを指定する */
  flex-direction: column;
  flex-grow: 1;
}

対応していることはdisplayをflexに、flex-directionをcolumnにしているだけです。先ほどと同様の対応でOKです。

以上がCSSのみで実現する方法になりますが、実はh3のように揃えたい要素が1つのときにしか基本的には使えません。一応使えないときのデモも「CSSで横並びで高さを厳密には揃えられない場合」の項目で載せております。

CSSだけで対応する強引なやり方もあるのですが、コードが複雑になってしまうため紹介はしません。後半で紹介するJSを用いた方法で対応するのが無難かと思います。

JSで横並びで折り返すカラムの行に応じて高さを揃える方法

今回は、PC幅は3カラム、SP幅は2カラムで対応しております。また、幅を伸び縮みさせた場合でも高さが揃うように対応させていただきました。
それでは見ていきましょう。

HTML詳細

<!-- JSで横並びで折り返すカラムの行に応じて高さを揃える場合 -->
<section>
  <div class="ly_inner">
    <h2>JSで横並びで折り返すカラムの行に応じて高さを揃える方法</h2>
    <p>レスポンシブ対応しています。見出し、テキスト、ボタンの外側にタグを挿入しても使用可能です。今回は入れていません。</p>
    <div class="bl_jsflex">
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text">テキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し見出し見出し</h3>
        <p class="el_text">テキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text">テキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title">見出し見出し</h3>
        <p class="el_text">テキストが入ります</p>
        <a href="">ボタン</a>
      </div>
    </div>
  </div>
</section>

こちらの変更点は、h3の見出しの下に、pのテキストが入ったことくらいです。その他は変更ありません。

CSS詳細

.bl_jsflex {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}

.bl_jsflex_item {
  width: calc((100% - 48px) / 3);
  background-color: #ccc;
}
.el_jsh3title {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
}
.el_text {
  font-size: 16px;
}
@media screen and (max-width: 767px) {
  .bl_jsflex_item {
    width: calc((100% - 24px) / 2);
  }
}

CSSも特に難しいことはしておりません。あげるとすればcalcのところでしょうか。
カラムの幅を計算しやすいように、100%の幅から余白分を引き、カラム数で割っているだけです。あまり難しいことはしておりません。

ちなみにCSSのみで対応したときは、子要素にflexなどを指定していましたが、今回は不要になります。混乱しないように注意しましょう。

JavaScript詳細

// JSで横並びで折り返すカラムの行に応じて高さを揃える場合
function resizeHandler() {
  const flexItems = document.querySelectorAll(".bl_jsflex_item");
  let itemsPerRow = 3; // デフォルトの1行あたりの要素数

  if (window.innerWidth <= 767) {
    itemsPerRow = 2; // 767px以下の場合、カラム数を2に設定
  }

  let currentRowItems = [];
  let currentRow = 0;

  flexItems.forEach((item, index) => {
    item.style.height = ""; // 元の高さをリセット

    currentRowItems.push(item);

    if ((index + 1) % itemsPerRow === 0 || index === flexItems.length - 1) {
      adjustHeight(currentRowItems);
      currentRowItems = [];
      currentRow++;
    }
  });
}

function adjustHeight(targets) {
  let maxTitleHeight = 0;
  let maxTextHeight = 0;

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = ""; // タイトルの高さをリセット
    text.style.height = ""; // テキストの高さをリセット

    maxTitleHeight = Math.max(maxTitleHeight, title.clientHeight);
    maxTextHeight = Math.max(maxTextHeight, text.clientHeight);
  });

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = maxTitleHeight + "px";
    text.style.height = maxTextHeight + "px";
  });
}

window.addEventListener("resize", resizeHandler);
window.addEventListener("load", resizeHandler);

ここが難解ですが、解説していきます。

function resizeHandler(){
  // 省略
}

function adjustHeight(targets){
  // 省略
}

まず上記の大枠についてですが、関数を定義するために書いております。resizeHandlerやadjustHeight、targetsは任意の名前にしても動きます。
ただし、名前を変更した場合は、中の同じ文字列の名前も変更するようにしてください。

resizeHandlerの中身について

const flexItems = document.querySelectorAll(".bl_jsflex_item");
  let itemsPerRow = 3; // デフォルトの1行あたりの要素数

  if (window.innerWidth <= 767) {
    itemsPerRow = 2; // 767px以下の場合、カラム数を2に設定
  }

constでflexItems(任意の名前で可)の変数を定義しています。bl_jsflex_itemのclass全てに対して指定しています。

letでitemsPerRowを3と定義しています。これはPC幅の1行に対しての要素数が3の時に動くようにするためのものです。
ここに関連して、if文の中ではitemsPerRowを2と指定しています。これはSP幅の時に2カラムになるため、このときに動くようにするための指定です。767の値は適宜変更してください。

続いて下記に移ります。

let currentRowItems = [];
  let currentRow = 0;

  flexItems.forEach((item, index) => {
    item.style.height = ""; // 元の高さをリセット

    currentRowItems.push(item);

    if ((index + 1) % itemsPerRow === 0 || index === flexItems.length - 1) {
      adjustHeight(currentRowItems);
      currentRowItems = [];
      currentRow++;
    }
  });

まずcurrentRowItemsで空の配列を定義します。続いてcurrentRowで1番目から始まるということを定義します。

flexItemsに対してforEachで繰り返し処理をしていきます。引数にはitemとindex(どちらも任意の名前で可)を入れています。

まず読み込まれたときの元の高さを「item style height」のところでリセットしていきます。

currentRowItemsをpushすることで、先ほど空にしていたcurrentRowItemsの配列の中に入っていきます。

if文の左側「(index + 1) % itemsPerRow === 0」について

まず、indexというのは0、1、2、3…のように、JavaScriptの場合は0から始まるようになっています。しかしHTMLでは0から始まりません。1、2、3…のように1から始まります。
このズレを解消するために、「index+1」としています。

あとは%は割り算をした余りを表しますので、PC幅の場合は下記のようになります。

  • 1番目:(0+1) ÷ 3 = 1/3(余りは1)
  • 2番目:(1+1) ÷ 3 = 2/3(余りは2)
  • 3番目:(2+1) ÷ 3 = 3(余りは0)

つまり「=== 0」の条件を満たすのは、余りが0となる3番目、6番目、9番目のように3の倍数の時になります。

if文の右側「index === flexItems.length – 1)」について

さきほどindexについては解説しましたので省略しますが、今度はlengthというものが出てきました。これはflexItemsの合計数を表しています。-1としているのは、indexのずれと合わせるためです。

以上のifの左側の条件を満たしていれば記述されている内容を探しに行き、左側の条件を満たしていなくても右側の条件を満たしている場合は、if文の中に記述されている内容を探しに行きます。

adjustHeightの部分は、別の場所で定義した関数を引っ張ってきています。後ほど出てきますが、その内容をcurrentRowItemsの配列の中に入れております。

続いてcurrentRowItemsに対し、また空の配列を入れ直しているのは、現在の行の対応が終わったため、次の新しい行の対応を始められるようにするためです。リセットしていると捉えるといいかと思います。

最後のcurrentRow++については、新しい行の対応に移るために増やしています。最初のように0から始まりませんので、カウントを増やしていくイメージです。

adjustHeightの中身について

  let maxTitleHeight = 0;
  let maxTextHeight = 0;

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = ""; // タイトルの高さをリセット
    text.style.height = ""; // テキストの高さをリセット

    maxTitleHeight = Math.max(maxTitleHeight, title.clientHeight);
    maxTextHeight = Math.max(maxTextHeight, text.clientHeight);
  });

  targets.forEach((target) => {
    const title = target.querySelector(".el_jsh3title");
    const text = target.querySelector(".el_text");

    title.style.height = maxTitleHeight + "px";
    text.style.height = maxTextHeight + "px";
  });

今回は、高さを揃えたい箇所がh3とpの2箇所あります。そのためそれぞれの高さを最初は0にするため、maxTitleHeight、maxTextHeightの2つ用意しています。

先ほどと同様に、forEachを使い繰り返し処理を行っていきます。

constでtitle、textをそれぞれ定義し、その高さをリセットします。

あとはそれぞれ0であるmaxTitleHeight、maxTextHeightの値が、順番に繰り返し処理されていく時に値が変わっていくように宣言しています。Math.maxは2つの値を比較して値が大きい方を採用します。
maxTitleHeightで例えてみると、下記のような感じになります。

  • 1回目の高さ:maxTitleHeight→0、title.clientHeight→20px 20pxを採用
  • 2回目の高さ:maxTitleHeight→20px、title.clientHeight→10px 20pxを採用
  • 3回目の高さ:maxTitleHeight→20px、title.clientHeight→40px 40pxを採用
  • 4回目の高さ:maxTitleHeight→40px、title.clientHeight→20px 40pxを採用

このような感じで、高い値を採用するとmaxTitleHeightの値がどんどん書き換わっていきます。これにより高さの最大値が得られるようになります。これで1つ目のforEachが終わります。

続いて2つ目のforEachです。本当はまとめて記載したいところでしたが、上手く動かなかったため処理を分けることにしました。constの定義は先ほどと同様ですので解説は省きます。

title.style.height = maxTitleHeight + "px";
text.style.height = maxTextHeight + "px";

上記については、el_jsh3titleとel_textの高さを指定するために記述しています。
maxTitleHeightとmaxTextHeightの最大値が入ることで、横に並んだときの高さが揃うようになります。

これでadjustHeightの中身が完成し、最初の方に出てきた下記の場所に入るように指定ができます。

adjustHeight(currentRowItems);

最後の下記部分は、resizeHandlerの関数を、ウィンドウがリサイズされたときに呼び出されるとき(resize)、ページの読み込みが完了した時点で呼びされるとき(load)で設定しています。

window.addEventListener("resize", resizeHandler);
window.addEventListener("load", resizeHandler);

これで以上となります。

JSで横並びで折り返さないカラムで高さを揃える方法

こちらはスライダーやカルーセルなどで結構使えるものかと思います。それでは見ていきましょう。

HTML詳細

<!-- JSで横並びで折り返さないカラムで高さを揃える場合 -->
<section>
  <div class="ly_inner">
    <h2>JSで横並びで折り返さないカラムで高さを揃える方法</h2>
    <p>スライダーやカルーセルの場合でも使えます。</p>
    <div class="bl_jsflex02">
      <div class="bl_jsflex_item02">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text02">テキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item02">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title02">見出し見出し見出し見出し</h3>
        <p class="el_text02">テキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
      <div class="bl_jsflex_item02">
        <img src="https://picsum.photos/640/360" alt="画像1" width="640" height="360">
        <h3 class="el_jsh3title02">見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し見出し</h3>
        <p class="el_text02">テキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        <a href="">ボタン</a>
      </div>
    </div>
  </div>
</section>

こちらは前回とほぼ同様です。クラス名だけ変更しています。

CSS詳細

.bl_jsflex02 {
  display: flex;
  gap: 24px;
}
.bl_jsflex_item02 {
  width: calc((100% - 48px) / 3);
  background-color: #ccc;
}
.el_jsh3title02 {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 16px;
}
.el_text02 {
  font-size: 16px;
}

こちらも前回とほぼ同様です。クラス名だけ変更しています。

JavaScript詳細

// JSで横並びで折り返さないカラムで高さを揃える場合
function resizeHandler02() {
  const title = document.querySelectorAll(".el_jsh3title02");
  const text = document.querySelectorAll(".el_text02");

  let maxTitleHeight = 0;
  let maxTextHeight = 0;

  title.forEach((target) => {
    target.style.height = "";
    const height = target.clientHeight;
    maxTitleHeight = Math.max(maxTitleHeight, height);
  });

  text.forEach((target) => {
    target.style.height = "";
    const height = target.clientHeight;
    maxTextHeight = Math.max(maxTextHeight, height);
  });

  title.forEach((target) => {
    target.style.height = maxTitleHeight + "px";
  });

  text.forEach((target) => {
    target.style.height = maxTextHeight + "px";
  });
}

window.addEventListener("resize", resizeHandler02);
window.addEventListener("load", resizeHandler02);

こちらも前回のコードが理解できていると、問題なく読んでいけるかと思いますので、解説は省略いたします。

これで以上となります。長いようですが、コードがたくさん貼ってあるだけです。
毎回調べるのが手間だなと思い書かせていただきました。参考にしていただけますと嬉しいです。