用意するhtmlは1つだけ!中身だけ個別に付与したカードを何枚も並べたセクションをつくる方法

用意するhtmlは1つだけ!中身だけ個別に付与したカードを何枚も並べたセクションをつくる方法
カードが複数ならぶUIというのは、昨今のWebサイトでよく見かけますよね。

例えば、
・新着情報セクション
・記事メディアなどで、新しい記事が並ぶセクション
・サムネイルとタイトル・抜粋を見せて詳細に飛ばすカードを並べたお知らせセクション

などなど。

今回は、
「HTML上には1枚だけのカード」を置き、「それを複製」していき、
「用意したコンテンツ用配列の要素の数」のぶんだけ、
「カードが配置」されたセクションをつくる、

という方法について、記事を書いていきます。

HTML

さて早速、HTMLです。HTML内に置いておくカードは、
これ1つだけです。親要素の中に、カード1つです。至ってシンプルですね。

<div class="my-container">
  <div id="baseCard" class="my-card">
    <img class="my-thumbnail" src="" alt="画像の説明" />
    <p class="my-title">1. WordPressについて</p>
    <a class="my-link" href="">詳細を見る</a>
  </div>
</div>

JavaScript

続いてJavaScript側です。この記述で、上の1枚のカードを複製して、
カードが並ぶセクションをつくっていきます。

コードのあとのセクションで、コメントアウトに対応する形で、何が書かれているかを記載してあります。

<script>
// [1]. 各カードの内容の配列。この要素の数ぶんだけカードが作成されます(この例だと5枚)
const targetCardArray = [
  {
    id: 1,
    title: "1. WordPressについて",
    thumbnailUrl: "https://picsum.photos/id/62/800/370",
    linkUrl: "/page-one/",
  },
  {
    id: 2,
    title: "2. CSSについて",
    thumbnailUrl: "https://picsum.photos/id/202/800/370",
    linkUrl: "/page-two/",
  },
  {
    id: 3,
    title: "3. デスクトップ整頓のコツ",
    thumbnailUrl: "https://picsum.photos/id/60/800/370",
    linkUrl: "/page-three/",
  },
  {
    id: 4,
    title: "4. カードタイトル",
    thumbnailUrl: "https://picsum.photos/id/88/800/370",
    linkUrl: "/page-four/",
  },
  {
    id: 5,
    title: "5. Card title",
    thumbnailUrl: "https://picsum.photos/id/108/800/370",
    linkUrl: "/page-five/",
  },
];

// [2]. 複製元のカードを取得する
const targetParent = document.querySelector(".my-container");
const targetCard = document.querySelector(".my-card");
const baseCard = document.querySelector("#baseCard");

// [3]. カードを複製して値を入れていく処理
targetCardArray.map((v) => {
  // [3-1]. 子要素の構造ごと複製するためtrueでcloneNode
  const cloneElement = targetCard.cloneNode(true);

  // [3-2]. cloneNode で複製するとHTMLCollectionとなるので配列に変換します
  const cloneElementChildren = [...cloneElement.children];

  // [3-3]. 複製した要素のIDが重複するためIDを振りなおします
  cloneElement.setAttribute("id", `card${v.id}`);

  // [3-4]. switch文で、条件に当てはまるカード内の要素に値を代入していく
  cloneElementChildren.map((e) => {
    switch (e.className) {
      case "my-title":
        e.textContent = v.title;
      case "my-thumbnail":
        e.src = v.thumbnailUrl;
        e.alt = v.title;
      case "my-link":
        e.href = v.linkUrl;
      default: break;
    }

    // [3-5]. if ~ elseで書く場合。この方法でも動きます
    // if(e.className == "my-title") {
    //   e.textContent = v.title;
    // } else if (e.className == "my-thumbnail") {
    //   e.src = v.thumbnailUrl;
    //   e.alt = v.title;
    // } else if (e.className == "my-link") {
    //   e.href = v.linkUrl;
    // }

  });

  // [3-6]. 取得しておいた親要素targetParentにカードを配置する
  targetParent.appendChild(cloneElement);
});

// [3-7]. 複製元のカードを削除して、配列の要素の数だけカードを配置完了
baseCard.remove();
</script>

上のJavaScript、何が書かれているか

さて、上のセクションのJavaScriptについて、何が書いてあるか順を追って記載します。
各番号はコード内のコメントアウトと対応していますので、参照するとわかりやすいかと思います。

[1]. まず最初に targetCardArray として、カードの内容を配列で定義しておきます。ここで定義した項目の数だけ、カードが生成される仕組みです。

[2]. 複製元となる、冒頭のセクションで書いたシンプルなHTML構造を取得して変数に格納しておきます。
上から順に
・カードの親要素、
・複製元となるカードの要素、
・初期配置されているカードのID(これは末尾で消去するため)
になります。

[3]. 続いて、カードを複製して値を入れていく処理です。

[3-1]. は、[2].で取得しておいたカードを cloneNode(true) で複製しています。
cloneNode() は、引数trueなら子要素も含めて全て複製できるため、trueを指定します。

[3-2]. は、上で複製した要素を「配列に変換」している記述です。
cloneNode() で複製すると HTMLCollection という型で複製され、そのままではループ処理がかけられないため、配列に変換します。

[3-3]. 複製した要素は、IDもそのまま複製されてくるため、ID重複します。
そのためここでIDを振りなおします。

[3-4]. switch文で、条件に当てはまるカード内の要素に値を代入していきます。
class名が一致する子要素を探し、そこに値を代入していく記述です。

[3-5]. ここはコメントアウトしていますが、上のswitch文を if ~ else で書きかえても動く、という例です。お好みで、どちらを使っても動きます。

[3-6]. 工程[2].で取得しておいた親要素 targetParent にカードを配置する記述です。cloneNode() しただけではどこにも配置されないので、targetParent.appendChild(cloneElement); として配置する場所を決めてあげます。

[3-7]. 仕上げに、複製元のカードを削除します。これをしないと初期配置のカードが残ってしまうので、 baseCard.remove(); とします。

これで、
「HTML上には1枚だけのカード」
「それを複製して」
「用意した配列の要素の数だけ」
「カードが配置されたセクションをつくる」

ということが完了します。

CSS

さて、CSSです。こちらはお好みで。サンプルなので最低限のスタイルをあてています。

.my-container {
  display: flex;
  gap: 1.5rem 1rem;
  justify-content: flex-start;
  flex-wrap: wrap;
  margin: 5rem auto;
  width: 800px;
}
.my-card {
  background: #fff;
  border-radius: 0.5rem;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5);
  box-sizing: border-box;
  overflow: hidden;
  padding: 0 0 1rem;
  width: calc(33.333% - 2rem);
}
.my-thumbnail {
  display: block;
  width: 100%;
}
.my-title {
  display: block;
  padding: 0 1rem;
}
.my-link {
  align-items: center;
  background: #323232;
  border-radius: 999px;
  color: #fff;
  display: flex;
  font-size: 0.6875rem;
  justify-content: center;
  height: 36px;
  margin: 0 1rem 0 auto;
  padding: 0 1rem;
  text-decoration: none;
  width: fit-content;
}

この記事で登場したcloneNode()などの解説

要素を複製する際に使用している cloneNode() について、MDNの詳しい解説があります。

cloneNode() メソッド – Web API | MDN

いつも書いていますが、MDNはMozillaが運営している、Webデザイン・Web制作に関するとても充実したリファレンスサイトです。
ぜひ一読をおすすめします。

この記事のまとめ

今回は、
「HTML上には1枚だけのカード」を置き、「それを複製」していき、
「用意したコンテンツ用配列の要素の数」のぶんだけ、
「カードが配置」されたセクションをつくる、

という方法について、記事を書いてみました。

前回のモーダルの記事と同様、
この方法や発想というのは、実は
Vue.js や React などのフロントエンドフレームワークを使ったWeb制作においては、いたって普通に使われている手法になります。
前回も書きましたが、

・コンテンツと見た目は分離させて管理する。
・見た目はコンポーネントとして使い回す。再利用する。
・コンテンツはそこに差し込んでいく。分離されているのでメンテナンスが容易。

↑というような概念です。

それをあえて静的なhtmlで実現するなら…という観点で、今回この記事を書きました。

これも前回書きましたが、実際の制作現場では
「Vue.js や React などのフロントエンドフレームワークを使うことができない案件 / 使わせてもらえない案件」
というのは多々存在します。
特に、単発のランディングページやキャンペーンページなどで、けっこうあります。

そういう仕様のページ(単なる静的ページ)でありながら、けっこう更新は頻繁に行われたりするケースも多いです。

そんな時に、「コンテンツと見た目を分離させて管理」しておくと、何かと便利なわけです。

今回の例は
JavaScript内にカードの配列を持たせていますが、これを「外部JSONファイル」として保存しておき、それを読み込んで使用するような方法をとれば、

「キャンペーン内容を更新したい!」
という依頼に対して、
「この data.json というファイルの、この行を書き換えれば誰でも更新できる」

という様な運用をすることも可能になる、というわけです。

もちろん、そもそもCMSが入っていたり、Vue.js や React などのフロントエンドフレームワークを使うことができる案件なら、それを使えば良い話です。でもそうではない案件もけっこうあって、そんな時。
すこしでも効率化が図れるなら…と思い、この記事を書いてみました。

この記事が皆さんのより良いWeb制作体験につながれば、嬉しく思います。

※この記事は内容の品質向上を目的に、随時更新を行う場合があります。

この記事をシェアする: