2026年5月20日

2026年5月20日

WordPressでアコーディオンメニューを実装する方法

はじめに

アコーディオンUIは、クリックで開閉するコンテンツ折りたたみ機能です。FAQページや長いコンテンツの整理に使われます。WordPressでは details / summary タグ(HTML標準)か、JavaScriptで実装できます。

症状・原因

  • FAQをアコーディオン形式で表示したい
  • クリックで開閉するコンテンツを作りたい
  • アニメーション付きのスムーズな開閉を実装したい
  • ショートコードでエディタから追加したい

解決手順

ステップ1:HTMLのdetails/summaryで最もシンプルに実装

<!-- テンプレートに直接記述 -->
<details class="accordion-item">
    <summary class="accordion-title">質問:WordPressとは何ですか?</summary>
    <div class="accordion-content">
        <p>WordPressはPHPで作られたオープンソースのCMSです。世界のウェブサイトの約43%で使用されています。</p>
    </div>
</details>
/* style.css */
.accordion-item {
    border: 1px solid #c3c4c7;
    border-radius: 4px;
    margin-bottom: 8px;
}

.accordion-title {
    padding: 16px 20px;
    cursor: pointer;
    font-weight: 600;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    user-select: none;
}

.accordion-title::after {
    content: '+';
    font-size: 18px;
    color: #2271b1;
    transition: transform 0.2s;
}

.accordion-item[open] .accordion-title::after {
    transform: rotate(45deg);
}

.accordion-content {
    padding: 0 20px 16px;
    color: #50575e;
}

ステップ2:スムーズなアニメーション付き(JavaScript版)

// js/accordion.js
document.addEventListener('DOMContentLoaded', function() {

    document.querySelectorAll('.js-accordion').forEach(function(item) {
        const title   = item.querySelector('.accordion-title');
        const content = item.querySelector('.accordion-content');
        if (!title || !content) return;

        // 初期状態で高さを0にする
        content.style.height = '0';
        content.style.overflow = 'hidden';
        content.style.transition = 'height 0.3s ease';

        title.setAttribute('aria-expanded', 'false');
        title.setAttribute('role', 'button');

        title.addEventListener('click', function() {
            const isOpen = item.classList.contains('is-open');

            if (isOpen) {
                // 閉じる
                content.style.height = content.scrollHeight + 'px';
                requestAnimationFrame(function() {
                    content.style.height = '0';
                });
                item.classList.remove('is-open');
                title.setAttribute('aria-expanded', 'false');
            } else {
                // 開く
                content.style.height = content.scrollHeight + 'px';
                item.classList.add('is-open');
                title.setAttribute('aria-expanded', 'true');

                // アニメーション終了後にhighを自動にする(リサイズ対応)
                content.addEventListener('transitionend', function handler() {
                    if (item.classList.contains('is-open')) {
                        content.style.height = 'auto';
                    }
                    content.removeEventListener('transitionend', handler);
                });
            }
        });
    });
});

ステップ3:FAQショートコードとして実装

// functions.php
add_shortcode('faq', function(array $atts, ?string $content = null): string {
    return '<div class="faq-list">' . do_shortcode($content ?? '') . '</div>';
});

add_shortcode('faq_item', function(array $atts, ?string $content = null): string {
    $atts = shortcode_atts(['q' => ''], $atts, 'faq_item');
    $question = esc_html($atts['q']);
    $answer   = wp_kses_post(do_shortcode($content ?? ''));

    return sprintf(
        '<div class="accordion-item js-accordion">
            <div class="accordion-title">%s</div>
            <div class="accordion-content">%s</div>
        </div>',
        $question,
        $answer
    );
});

使用例(投稿エディタで):

[faq]
[faq_item q="WordPressのバックアップはどうすれば良いですか?"]
UpdraftPlusプラグインを使って定期的にバックアップすることを推奨します。
[/faq_item]
[faq_item q="プラグインは何個まで使えますか?"]
公式には制限はありませんが、20〜30個を目安に必要なものだけ使うのがベストプラクティスです。
[/faq_item]
[/faq]

ステップ4:schema.org FAQPageのJSON-LDを出力する

// functions.php — FAQページの構造化データ
add_shortcode('faq', function(array $atts, ?string $content = null): string {
    // FAQアイテムを収集(実際の実装では別途パース処理が必要)
    $html = '<div class="faq-list">' . do_shortcode($content ?? '') . '</div>';

    // JSON-LD(静的サンプル)
    $schema = json_encode([
        '@context' => 'https://schema.org',
        '@type'    => 'FAQPage',
        'mainEntity' => [
            ['@type' => 'Question', 'name' => 'Q1', 'acceptedAnswer' => ['@type' => 'Answer', 'text' => 'A1']]
        ]
    ], JSON_UNESCAPED_UNICODE);

    return $html . '<script type="application/ld+json">' . $schema . '</script>';
});

注意事項

  • details/summary タグはJavaScript不要で動作しますが、アニメーションの細かい制御が難しいです。アニメーションが必要な場合はJavaScript版を使用してください
  • JavaScript版でアコーディオンを複数開けないようにするには、クリック時に他のアコーディオンを全て閉じる処理を追加します
  • FAQページでは schema.org/FAQPage の構造化データを実装するとGoogleのリッチリザルトに表示される場合があります

まとめ

最もシンプルな実装は

/

タグのみ(JS不要)です。アニメーションが必要な場合はJavaScriptで scrollHeight を使って動的に高さを変化させます。アクセシビリティのために aria-expandedtrue/false で切り替えてください。

お気軽にご相談ください

お見積りへ お問い合わせへ