[js]WPのカスタム投稿を外部サイトで取得して出力する方法

WP REST APIのやつ。

やりたいこと

タイトルの通り。

身近なところでいえば、カラーミーショップはWPを無料で使える(プランによる)ようになっている。
カラーミーショップのお知らせ機能ははっきり言ってダメダメなので、WPに任せるのが良いよね。
だけど、同ドメイン下で使えるだけで連携らしい連携はない。
だからWPの機能を使って取得するようにしましょうねという話。

WP REST APIのお陰で外部からWPの記事を引っ張ることができる。
jsで拾えるので、実質的にどのWEBサイト環境でも出力可能ということになりますね。

やっていく

完成イメージ

カスタム投稿「news」を作ってるとして。

HTML上にこんなのを設置してたら

<div class="news">
</div>

jsでこうなるようにする。

<div class="news">
    <ul class="news_list">
        <li><a href="【記事URL】"><div class="date"><time datetime="【投稿日】">【投稿日】</time></div><div class="ttl">【記事タイトル】</div></a></li>
        <li><a href="【記事URL】"><div class="date"><time datetime="【投稿日】">【投稿日】</time></div><div class="ttl">【記事タイトル】</div></a></li>
        <li><a href="【記事URL】"><div class="date"><time datetime="【投稿日】">【投稿日】</time></div><div class="ttl">【記事タイトル】</div></a></li>
        <li><a href="【記事URL】"><div class="date"><time datetime="【投稿日】">【投稿日】</time></div><div class="ttl">【記事タイトル】</div></a></li>
        <li><a href="【記事URL】"><div class="date"><time datetime="【投稿日】">【投稿日】</time></div><div class="ttl">【記事タイトル】</div></a></li>
    </ul>
</div>

新着5件取りましょう。

バニラjsとjQueryどっちがいいか

どっちでもやれる。

ぶっちゃけ好み。

document.addEventListener('DOMContentLoaded', () => {
    // 1. WordPress REST APIのエンドポイントURL
    const apiUrl = '【WordPress アドレス】/wp-json/wp/v2/news?per_page=5';
    // 2. 挿入先となるターゲット要素
    const newsContainer = document.querySelector('.news');

    // ターゲット要素が存在しない場合は処理を中断
    if (!newsContainer) return;

    fetch(apiUrl)
        .then(response => {
            if (!response.ok) throw new Error('Network response was not ok');
            return response.json();
        })
        .then(posts => {
            // ul要素を生成してクラスを付与
            const ul = document.createElement('ul');
            ul.className = 'news_list';

            if (posts.length === 0) {
                ul.innerHTML = '<li>お知らせはありません。</li>';
                newsContainer.appendChild(ul);
                return;
            }

            // 記事データをループ処理してli要素を組み立てる
            posts.forEach(post => {
                const title = post.title.rendered; // 記事タイトル
                const link = post.link; // 記事URL
                
                // 日付のフォーマット処理 (YYYY.MM.DD形式など)
                const dateObj = new Date(post.date);
                const year = dateObj.getFullYear();
                const month = String(dateObj.getMonth() + 1).padStart(2, '0');
                const day = String(dateObj.getDate()).padStart(2, '0');
                
                const formattedDate = `${year}/${month}/${day}`; // 表示用 (例: 2026.06.05)
                const datetimeAttr = post.date.split('T')[0]; // datetime属性用 (例: 2026-06-05)

                // li要素の生成
                const li = document.createElement('li');
                li.innerHTML = `
                    <a href="${link}">
                        <div class="date">
                            <time datetime="${datetimeAttr}">${formattedDate}</time>
                        </div>
                        <div class="ttl">${title}</div>
                    </a>
                `;
                ul.appendChild(li);
            });

            // 最後に<div class="news">の中に<ul>を丸ごと挿入
            newsContainer.appendChild(ul);
        })
        .catch(error => {
            console.error('記事の取得に失敗しました:', error);
            newsContainer.innerHTML = '<ul class="news_list"><li>記事の読み込みに失敗しました。</li></ul>';
        });
});

バニラだから</body>直前に配置、<head>内で読ませるならdeferつけて発火タイミングを遅らせるとか。

jQueryだとこんな感じ。

$(function() {
    // 1. WordPress REST APIのエンドポイントURL
    var apiUrl = '【WordPress アドレス】/wp-json/wp/v2/news';
    // 2. 挿入先となるターゲット要素
    var $newsContainer = $('.news');

    if ($newsContainer.length === 0) return;

    $.ajax({
        url: apiUrl,
        type: 'GET',
        dataType: 'json',
        data: { per_page: 5 }, // 取得件数
        success: function(posts) {
            // ul要素を生成
            var $ul = $('<ul>').addClass('news_list');

            if (posts.length === 0) {
                $ul.append('<li>お知らせはありません。</li>');
                $newsContainer.append($ul);
                return;
            }

            // 記事データをループ処理
            $.each(posts, function(index, post) {
                var title = post.title.rendered;
                var link = post.link;
                
                // 日付処理
                var dateObj = new Date(post.date);
                var year = dateObj.getFullYear();
                var month = ('0' + (dateObj.getMonth() + 1)).slice(-2);
                var day = ('0' + dateObj.getDate()).slice(-2);
                
                var formattedDate = year + '/' + month + '/' + day; // 表示用
                var datetimeAttr = post.date.split('T')[0]; // datetime属性用

                // フォーマット通りにHTMLを組み立て
                var liHtml = '<li>' +
                    '<a href="' + link + '">' +
                        '<div class="date">' +
                            '<time datetime="' + datetimeAttr + '">' + formattedDate + '</time>' +
                        '</div>' +
                        '<div class="ttl">' + title + '</div>' +
                    '</a>' +
                '</li>';

                $ul.append(liHtml);
            });

            // <div class="news">の中に挿入
            $newsContainer.append($ul);
        },
        error: function() {
            $newsContainer.html('<ul class="news_list"><li>記事の読み込みに失敗しました。</li></ul>');
        }
    });
});

注意とか

出力がワンテンポ遅れる。

だから画面がガクッとなる。

CSSで対策が必要。

高さが固定できたら良いんだけど、件名が長くて改行が発生したらとか考えたら安定しないかもしれない。

どんまい。

おまけ:データ量を制限する

WP REST APIは取得できる情報が膨大なのでお得な反面、不要なものも取得しまくってるといえる。だから情報を制限しちゃおうねという話。

パラメータに追記することで取得内容を限定できる。

//全部
【WordPress アドレス】/wp-json/wp/v2/news
//指定
【WordPress アドレス】/wp-json/wp/v2/news?per_page=5&_fields=id,title,link,date

プラグインとかで記事に紐づくなんやかやを増やした場合、jsonの内容にも追加されるのでとんでもない量になる。環境にもよるので都度自分で取得・展開してみて、何が引っ張れるかをチェックするのがいい。

いでに、表示が遅いことの対策にキャッシュを盛り込んだりもできる。
初回アクセスから一定期間はその時取得した内容を使い回す。
便利な半面、タイミングが合わないと最新情報が表示されないリスクもある。

まあ、こんな感じ。

const apiUrl = '【WordPress アドレス】/wp-json/wp/v2/news?per_page=5&_fields=id,title,link,date';
const newsContainer = document.querySelector('.news');

if (newsContainer) {
    const cacheKey = 'wp_news_cache';
    const cacheTimeKey = 'wp_news_cache_time';
    const cacheDuration = 10 * 60 * 1000; // キャッシュの有効期限:10分(ミリ秒換算)

    const cachedData = localStorage.getItem(cacheKey);
    const cachedTime = localStorage.getItem(cacheTimeKey);
    const now = Date.now();

    // 1. キャッシュが存在し、かつ期限内であればそれを使う(超高速表示)
    if (cachedData && cachedTime && (now - cachedTime < cacheDuration)) {
        renderNews(JSON.parse(cachedData));
    } else {
        // 2. キャッシュがない、または期限切れの場合はAPIから取得
        fetch(apiUrl)
            .then(response => {
                if (!response.ok) throw new Error('Network response was not ok');
                return response.json();
            })
            .then(posts => {
                // データをローカルストレージに保存
                localStorage.setItem(cacheKey, JSON.stringify(posts));
                localStorage.setItem(cacheTimeKey, now.toString());

                renderNews(posts);
            })
            .catch(error => {
                console.error('記事の取得に失敗しました:', error);
                // APIが落ちていても、古いキャッシュがあれば緊急用として表示する
                if (cachedData) {
                    renderNews(JSON.parse(cachedData));
                } else {
                    newsContainer.innerHTML = '<ul class="news_list"><li>記事の読み込みに失敗しました。</li></ul>';
                }
            });
    }
}

// 画面に描画する処理を関数に分離
function renderNews(posts) {
    const newsContainer = document.querySelector('.news');
    const ul = document.createElement('ul');
    ul.className = 'news_list';

    if (posts.length === 0) {
        ul.innerHTML = '<li>お知らせはありません。</li>';
        newsContainer.innerHTML = '';
        newsContainer.appendChild(ul);
        return;
    }

    posts.forEach(post => {
        const title = post.title.rendered;
        const link = post.link;

        const dateObj = new Date(post.date);
        const year = dateObj.getFullYear();
        const month = String(dateObj.getMonth() + 1).padStart(2, '0');
        const day = String(dateObj.getDate()).padStart(2, '0');

        const formattedDate = `${year}/${month}/${day}`;
        const datetimeAttr = post.date.split('T')[0];

        const li = document.createElement('li');
        li.innerHTML = `
            <a href="${link}">
                <div class="date">
                    <time datetime="${datetimeAttr}">${formattedDate}</time>
                </div>
                <div class="ttl">${title}</div>
            </a>
        `;
        ul.appendChild(li);
    });

    newsContainer.innerHTML = ''; // 既存のローディング表示などを消去
    newsContainer.appendChild(ul);
}

コメント

タイトルとURLをコピーしました