例えば、
・新着情報セクション
・記事メディアなどで、新しい記事が並ぶセクション
・サムネイルとタイトル・抜粋を見せて詳細に飛ばすカードを並べたお知らせセクション
などなど。
今回は、
「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制作体験につながれば、嬉しく思います。
※この記事は内容の品質向上を目的に、随時更新を行う場合があります。