HTMLとCSSとJavaScriptで作ったサイトにMicroCMSを使った投稿システムを実装する方法
はじめに:後からお知らせ機能を追加する課題
「すでに運用しているHTML/CSS/JavaScriptで作られたサイトに、後からお知らせ機能やニュース投稿システムを追加したい」というケースの際にはどうしてますか?
解決策としては「WordPressに完全移行する」というアプローチが一般的かと思います。
しかし、これには以下のような課題があります:
- WordPress用のテーマ開発に時間とコストがかかる
- 既存の機能をWordpress用として再実装する必要がある
特に後出しでこういう投稿機能をつけたいと言われた際には
もう一度構成を考えなくてはいけないのかと一度頭を抱えると思います。
そういった方を救うべく、この記事では、既存のHTML/CSS/JavaScriptサイトをそのまま活かしながら、MicroCMSというヘッドレスCMSを使ってお知らせ機能を簡単に実装する方法をご紹介します。WordPressへの完全移行に比べて、慣れれば少ない工数とシンプルな設計で実現できる方法です。
目次
- MicroCMSとは:ヘッドレスCMSの利点
- 必要な準備とMicroCMSのセットアップ
- MicroCMS JavaScript SDKの導入
- 実装例:お知らせ機能の組み込み
- トップページへの最新お知らせ表示
- お知らせ一覧ページの作成
- お知らせ詳細ページの作成
- 実装のTips
- APIキー取り扱いに関する注意点
- プレビュー機能の活用方法
1. MicroCMSとは:ヘッドレスCMSの利点
そもそもMicroCMSとは何かということですがWordpressというパッケージと異なり、MicroCMSの管理画面で投稿を入力し、それをJavaScriptで呼び出すことによって、使えるため独立性が高かくその部分が今回の要件とマッチしているために使用します。
なぜ既存サイトにMicroCMSが適しているのか?
- フロントエンドを自由に選べる:既存のHTML/CSS/JSをそのまま活かせる
- 最小限の変更で導入可能:サイト全体の再構築が不要
- APIベースの連携:必要なページやコンポーネントだけにCMS機能を追加できる
- 高速な開発:JavaScript SDKを利用して簡単に実装可能
2. 必要な準備とMicroCMSのセットアップ
必要なもの
- 既存のHTML/CSS/JavaScriptで作られたウェブサイト
- MicroCMSのアカウント(無料プランでエンドポイントを3つ使用可能)
- JavaScriptの基本的な知識
MicroCMSのセットアップ手順
- MicroCMS公式サイトからアカウント登録
- 新しいサービスの作成
- APIスキーマの設定(お知らせ用のコンテンツ型を定義)
- APIキーの取得
基本的には公式のこの記事に記載があるので登録していきましょう!
お知らせ用のAPIスキーマは以下のような項目を持つと良いと思います。
- タイトル(テキストフィールド)
- 内容(リッチエディタ)
- カテゴリ(セレクトフィールド)
- アイキャッチ画像(画像フィールド)
(公開日、更新日は自動で値の中に入りますので設定不要です。)
例ですが、上記ですとこのような画面になります。
※フィールド設定の図、ここで設定したtitleやcontentを使ってHTMLとしてJavascript経由で
表示していきます。念のため、タイプミス等には気をつけてください。
上記のように設定すると、入力画面ではこのようになります。
3. MicroCMS JavaScript SDKの導入
MicroCMSのJavaScript SDKを使うことで、よりシンプルにAPIを利用することができます。CDNを使って簡単に導入できます。
(SDKとは設定をやりやすくするために事業者が作っている便利パッケージです。)
<!-- HTMLのhead内またはbody終了タグの前に追加 -->
<script src="<https://unpkg.com/microcms-js-sdk@latest/dist/umd/microcms-js-sdk.js>"></script>
そして、このsdkを使用するためにはAPIキーが必要となりますので、こちらの公式ドキュメントの記事を参照してください。
4. 実装例:お知らせ機能の組み込み
A. トップページへの最新お知らせ表示
既存のトップページ(index.html)に最新のお知らせを表示するコードを追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/microcms-js-sdk@latest/dist/umd/microcms-js-sdk.js"></script>
<script src="js/top-news.js" defer></script>
<title>TopページのNews Sectionだけを抜粋</title>
</head>
<body>
<section class="news-section">
<div class="container">
<h2>お知らせ</h2>
<ul id="top-news" class="news-list">
<!-- JavaScriptで動的に内容が挿入されます -->
</ul>
<div class="news-more">
<a href="news.html">お知らせ一覧を見る</a>
</div>
</div>
</section>
</body>
</html>
次に、トップページにお知らせを表示するためのJavaScriptファイル(top-news.js)を作成します。
簡単に解説するとdocument.getElementById('top-news')でHTMLのDOM要素を読み取って、
それに対して、非同期でMicroCMSから読みっとったNews情報を入れています。
const { createClient } = microcms;
// サービスドメインはMicroCMSの管理画面で確認できる「XXXX.microcms.io」のXXXX部分
// APIキーは管理画面の「API管理 > API KEY > API KEY」からコピー
const client = createClient({
serviceDomain: 'あなたのサービスドメイン',
apiKey: 'あなたのAPIキー',
});
// 最新のお知らせを取得して表示する関数
async function displayTopNews() {
const newsContainer = document.getElementById('top-news');
if (!newsContainer) return; // 要素が存在しない場合は何もしない
// 最初に読み込み中の表示を入れる
newsContainer.innerHTML = '<li class="loading">読み込み中...</li>';
try {
// 最新3件のお知らせを取得
const response = await client.get({
endpoint: 'news',
queries: {
limit: 3,
orders: '-publishedAt', // 公開日の降順(新しい順)
},
});
if (response.contents.length === 0) {
newsContainer.innerHTML = '<li>現在お知らせはありません。</li>';
return;
}
// お知らせリストをmapで生成
const newsItems = response.contents.map((news) => {
const date = new Date(news.publishedAt).toLocaleDateString('ja-JP');
return `
<li class="news-item">
<a href="news-detail.html?id=${news.id}">
<span class="news-date">${date}</span>
${
news.category
? `<span class="news-category">${news.category}</span>`
: ''
}
<span class="news-title">${news.title}</span>
</a>
</li>
`;
});
// 生成したHTMLを挿入
newsContainer.innerHTML = newsItems.join('');
} catch (error) {
console.error('Error fetching news:', error);
newsContainer.innerHTML = '<li>お知らせの取得に失敗しました。</li>';
}
}
// ページ読み込み時にお知らせを表示
window.addEventListener('DOMContentLoaded', displayTopNews);
B. お知らせ一覧ページの作成
お知らせ一覧を表示するページ(news.html)を新規作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>お知らせ一覧 | 会社名</title>
<!-- 既存サイトのCSSを読み込み -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- 既存サイトのヘッダーをここに含める -->
<header>
<!-- 既存のヘッダーコード -->
</header>
<main>
<div class="page-header">
<div class="container">
<h1>お知らせ</h1>
</div>
</div>
<div class="container">
<!-- カテゴリフィルター(オプション) -->
<div id="category-filter" class="category-filter">
<!-- カテゴリが動的に表示されます -->
</div>
<!-- お知らせ一覧 -->
<div id="news-list">
<!-- ここにお知らせ一覧が動的に表示されます -->
<div class="loading">読み込み中...</div>
</div>
<!-- ページネーション -->
<div id="pagination" class="pagination">
<!-- ページネーションが動的に表示されます -->
</div>
</div>
</main>
<!-- 既存サイトのフッターをここに含める -->
<footer>
<!-- 既存のフッターコード -->
</footer>
<!-- MicroCMS SDKとJavaScriptを読み込み -->
<script src="<https://unpkg.com/microcms-js-sdk@latest/dist/umd/microcms-js-sdk.js>"></script>
<script src="js/news-list.js"></script>
</body>
</html>
次に、お知らせ一覧を取得して表示するJavaScriptファイル(news-list.js)を作成します。
主な機能としては一覧の取得とページネーションとサムネイルの最適化です。
<img class="news-thumbnail" src="${news.thumbnail.url}?w=120&h=80&fit=crop" alt="">`
ここのw=以下で行なっています。
詳細は以下、参照いただくのが良いですが、アップロードされた大きい画像から実際に使うサイズに切り取りをしてくれるので、アクセスしてからの表示を早くすることができます。
// news-list.js
// MicroCMS SDKの初期化
const { createClient } = microcms;
// サービスドメインはMicroCMSの管理画面で確認できる「XXXX.microcms.io」のXXXX部分
// APIキーは管理画面の「API→API KEY」からコピー
const client = createClient({
serviceDomain: 'あなたのサービスドメイン',
apiKey: 'あなたのAPIキー',
});
// URLパラメータを取得する関数
function getParam(name) {
const parsedUrl = new URL(window.location.href);
return parsedUrl.searchParams.get(name);
}
// お知らせ一覧を表示する関数
async function displayNewsList() {
const newsListContainer = document.getElementById('news-list');
const paginationContainer = document.getElementById('pagination');
// 初期表示は読み込み中
newsListContainer.innerHTML = '<li class="loading">読み込み中...</li>';
// ページの取得
const currentPage = parseInt(getParam('page')) || 1;
const limit = 10; // 1ページあたりの表示件数
try {
// クエリパラメータの設定
const queries = {
limit,
offset: (currentPage - 1) * limit,
orders: '-publishedAt',
};
// データ取得
const response = await client.get({
endpoint: 'news',
queries,
});
if (response.contents.length === 0) {
newsListContainer.innerHTML = '<li>お知らせが見つかりませんでした</li>';
return;
}
// お知らせリストの生成(mapを使用)
const newsItems = response.contents.map((news) => {
const date = new Date(news.publishedAt).toLocaleDateString('ja-JP');
return `
<li class="news-item">
<a href="news-detail.html?id=${news.id}">
<span class="news-date">${date}</span>
${
news.category
? `<span class="news-category">${news.category}</span>`
: ''
}
<span class="news-title">${news.title}</span>
${
news.thumbnail
? `<img class="news-thumbnail" src="${news.thumbnail.url}?w=120&h=80&fit=crop" alt="">`
: ''
}
</a>
</li>
`;
});
// リストを表示
newsListContainer.innerHTML = newsItems.join('');
// ページネーション
renderPagination(
paginationContainer,
response.totalCount,
currentPage,
limit
);
} catch (error) {
console.error('Error:', error);
newsListContainer.innerHTML = '<li>お知らせの取得に失敗しました</li>';
}
}
// ページネーションの表示
function renderPagination(container, totalCount, currentPage, limit) {
const totalPages = Math.ceil(totalCount / limit);
if (totalPages <= 1) return;
const items = [];
// 前へ
if (currentPage > 1) {
items.push(`<li><a href="?page=${currentPage - 1}">前へ</a></li>`);
}
// ページ番号
for (let i = 1; i <= totalPages; i++) {
if (i === currentPage) {
items.push(`<li class="current">${i}</li>`);
} else {
items.push(`<li><a href="?page=${i}">${i}</a></li>`);
}
}
// 次へ
if (currentPage < totalPages) {
items.push(`<li><a href="?page=${currentPage + 1}">次へ</a></li>`);
}
container.innerHTML = `<ul>${items.join('')}</ul>`;
}
// 初期化
window.addEventListener('DOMContentLoaded', () => {
displayNewsList();
});
C. お知らせ詳細ページの作成
お知らせの詳細を表示するページ(news-detail.html)を作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>お知らせ詳細 | 会社名</title>
<!-- 既存サイトのCSSを読み込み -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- 既存サイトのヘッダーをここに含める -->
<header>
<!-- 既存のヘッダーコード -->
</header>
<main>
<div class="container">
<div id="news-detail">
<!-- ここにお知らせ詳細が動的に表示されます -->
<div class="loading">読み込み中...</div>
</div>
<div class="news-nav">
<a href="news.html" class="btn-back">お知らせ一覧に戻る</a>
</div>
</div>
</main>
<!-- 既存サイトのフッターをここに含める -->
<footer>
<!-- 既存のフッターコード -->
</footer>
<!-- MicroCMS SDKとJavaScriptを読み込み -->
<script src="<https://unpkg.com/microcms-js-sdk@latest/dist/umd/microcms-js-sdk.js>"></script>
<script src="js/news-detail.js"></script>
</body>
</html>
次に、お知らせ詳細を取得して表示するJavaScriptファイル(news-detail.js)を作成します。
簡単にはなりますが、news-detail.htmlに推移したときにgetNewsIdの関数で取得し、そのIDをもとにコンテンツを呼び出して、詳細ページを表示しています。
// news-detail.js
// MicroCMS SDKの初期化
const { createClient } = microcms;
const client = createClient({
serviceDomain: 'あなたのサービスドメイン',
apiKey: 'あなたのAPIキー',
});
// URLからIDを取得する関数
function getNewsId() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('id');
}
// お知らせ詳細を表示する関数
async function displayNewsDetail() {
const newsDetailContainer = document.getElementById('news-detail');
const newsId = getNewsId();
if (!newsId) {
window.location.href = 'news.html';
return;
}
try {
// MicroCMS SDKを使用して特定のお知らせを取得
const news = await client.get({
endpoint: 'news',
contentId: newsId
});
// タイトルを設定
document.title = `${news.title} | 会社名`;
// お知らせ内容を表示
const date = new Date(news.publishedAt).toLocaleDateString('ja-JP');
newsDetailContainer.innerHTML = `
<article class="news-detail ${news.isImportant ? 'important' : ''}">
<div class="news-meta">
<time datetime="${news.publishedAt}">${date}</time>
${news.category ? `<span class="news-category">${news.category}</span>` : ''}
</div>
<h1>${news.title}</h1>
${news.eyecatch ? `<div class="news-eyecatch"><img src="${news.eyecatch.url}" alt=""></div>` : ''}
<div class="news-content">
${news.content}
</div>
</article>
`;
} catch (error) {
console.error('Error fetching news detail:', error);
newsDetailContainer.innerHTML = '<p>お知らせの取得に失敗しました。</p>';
}
}
// ページ読み込み時にお知らせ詳細を表示
window.addEventListener('DOMContentLoaded', displayNewsDetail);
5. 気をつけるべき点
APIキー取り扱いに関する注意点
クライアントサイドJavaScriptにAPIキーを記述する場合、以下の点に注意しましょう。
// APIキーはブラウザの開発者ツールから確認できます
const client = createClient({
serviceDomain: 'あなたのサービスドメイン',
apiKey: 'あなたのAPIキー', // このAPIキーは公開されます
});
MicroCMSの公式ドキュメントによれば、以下の条件を満たす場合はクライアントサイドでAPIキーを使用しても問題ありません
-
APIキーの権限を最小限に制限する
- GETのみの権限を付与し、POSTやPUT、DELETE、PATCHの権限は付与しない
-
公開しても問題ないコンテンツのみを扱う
- 全てのコンテンツが公開されても問題ない情報であること
より高いセキュリティが必要な場合は、私の場合はNext.jsでWebサイトを作成したりします。
また、下書きのプレビュー画面やカテゴリのフィルタリングなど追加で実装するのも
機能面では充実してきますね。
以上です。ここまでお読みいただきありがとうございました。
Discussion