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