目次
Ajax(非同期通信)で表示を更新する方法
リロード方式が「ページ全体を再読み込みしてクッキーの状態を反映させる」という標準的な手法であるのに対し、Ajax方式は「ページ遷移を伴わず、特定の要素だけを最新の状態に差し替える」高度な手法です。
通常、ウィジェットの表示設定はAjaxリクエスト時には保持されません。
しかし、本プログラムでは初回のウィジェット描画時にそれらの設定をサーバーのセッションへ一時保存し、Ajax呼び出し時に取り出して再適用する仕組みを構築しています。
これにより、リロードなしでもウィジェット固有のカスタマイズを維持したまま、滑らかな表示更新します。
実装手順
以下の手順で実装します。
- ラベルPHPを追加
以下のコードをfunctions.phpに追加します。
お気に入り投稿はライブラリ「jquery.cookie.js」を用い保存します。// フロント用JS・CSSを追加 add_action('wp_enqueue_scripts', function () { wp_enqueue_script('jquery-cookie', 'https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js', ['jquery'], '1.4.1', true); $ajax_url = admin_url('admin-ajax.php'); ob_start(); ?> jQuery(function ($) { var KEY = 'my_fav_page_list'; /** * お気に入りボタンの状態を更新 * * @return {void} */ function syncUI() { // クッキーからデータを取得 const currentCook = JSON.parse($.cookie(KEY) || '[]'); $('.fav-button').each(function () { const id = String($(this).data('id')); $(this).toggleClass('active', currentCook.indexOf(id) > -1); }); } /** * .favorite要素(Swiperコンテナ本体)に対してSwiperを初期化 * * @param {jQuery} $container 初期化対象の要素 * @return {void} */ function initFavoriteSwiper($container) { if (typeof Swiper === 'undefined') { return; } if (!$container.length || !$container.hasClass('swiper')) { return; } // 初期化済みの場合は処理対象外 if ($container[0].swiper) { return; } // Swiperインスタンスの初期化を設定 new Swiper($container[0], { slidesPerView: 'auto', spaceBetween: 30, navigation: { nextEl: $container.find('.swiper-button-next')[0], prevEl: $container.find('.swiper-button-prev')[0], }, observer: true, observeParents: true, }); } /** * お気に入り一覧をAjaxで更新 * * @return {void} */ function refreshFavWidget() { const $favoriteArea = $('.favorite'); if (!$favoriteArea.length) { return; } $.post('<?php echo esc_url($ajax_url); ?>', { action: 'refresh_fav_list' }, function (response) { if (!response) { return; } // 差し替え前に、既存のSwiperインスタンスを破棄 $favoriteArea.each(function () { if (this.swiper) { this.swiper.destroy(true, true); } }); // DOMを差し替え const $newContent = $($.trim(response)); $favoriteArea.replaceWith($newContent); syncUI(); // 新しいコンテナに対してSwiperを再初期化 initFavoriteSwiper($newContent); }); } // お気に入りボタンクリック $(document).on('click', '.fav-button', function (e) { e.preventDefault(); e.stopPropagation(); // クッキーからデータを取得 const $btn = $(this); const id = String($btn.data('id')); let cook = JSON.parse($.cookie(KEY) || '[]'); const index = cook.indexOf(id); // お気に入り状態を変更 if (index > -1) { cook.splice(index, 1); } else { cook.push(id); } // クッキーを更新 $.cookie(KEY, JSON.stringify(cook), { expires: 365, path: '/' }); // お気に入り一覧の更新を通知 $(document).trigger('favListUpdated'); }); // お気に入り一覧更新時に再描画 $(document).on('favListUpdated', function () { syncUI(); refreshFavWidget(); }); // クッキーの有効期限を更新 const initialCook = JSON.parse($.cookie(KEY) || '[]'); if (initialCook.length > 0) { $.cookie(KEY, JSON.stringify(initialCook), { expires: 365, path: '/' }); } syncUI(); }); <?php wp_add_inline_script('jquery-cookie', ob_get_clean(), 'after'); }); /** * Ajaxでお気に入り一覧を出力 * * @return void */ if (!function_exists('ajax_refresh_fav_list')): function ajax_refresh_fav_list() { echo do_shortcode('[new_list action="favorite"]'); wp_die(); } endif; // Ajaxアクションを追加 add_action('wp_ajax_refresh_fav_list', 'ajax_refresh_fav_list'); add_action('wp_ajax_nopriv_refresh_fav_list', 'ajax_refresh_fav_list'); // 新着記事のクエリを設定 add_filter('widget_entries_args', function ($args) { if (empty($GLOBALS['favorite'])) { return $args; } $ids = []; // クッキーからお気に入りIDを取得 if (isset($_COOKIE['my_fav_page_list'])) { $ids = json_decode(stripslashes($_COOKIE['my_fav_page_list']), true); } // お気に入り記事がない場合は終了 if (empty($ids) || !is_array($ids)) { $args['post__in'] = [0]; $args['posts_per_page'] = 0; } else { $ids = array_unique(array_map('intval', (array) $ids)); $args['post__in'] = array_reverse($ids); $args['orderby'] = 'post__in'; } $args['ignore_sticky_posts'] = true; return $args; }); // テンプレート(エントリーカード)を変更 add_filter('cocoon_part__tmp/entry-card', function ($content) { ob_start(); get_fav_button(); $button = ob_get_clean(); return preg_replace('/(<h2[^>]*>)/', '$1' . $button, $content); }); /** * お気に入りボタンを出力 * * @return void */ if (!function_exists('get_fav_button')): function get_fav_button() { echo '<span class="fav-button" data-id="' . get_the_ID() . '" title="お気に入り"><i class="fas fa-paperclip"></i></span>'; } endif; // 新着記事の属性を設定 add_filter('shortcode_atts_generate_widget_entries_tag', function ($out, $pairs, $atts) { if (!session_id() && !headers_sent()) { session_start(); } if ( empty($GLOBALS['favorite']) && (!isset($out['action']) || $out['action'] !== 'favorite') ) { return $out; } // 初回の設定をセッションに保存 if (!wp_doing_ajax()) { $_SESSION['my_fav_page_list_atts'] = $out; } // Ajax再描画時に復元 elseif (isset($_SESSION['my_fav_page_list_atts'])) { $out = $_SESSION['my_fav_page_list_atts']; } $out['class'] .= ($out['class'] ? ' ' : '') . 'favorite'; $GLOBALS['favorite'] = true; add_action('widget_entry_card_date_before', 'get_fav_button', 10, 2); // 新着記事終了時に状態をリセット add_filter('do_shortcode_tag', function ($output, $tag) { if ($tag === 'new_list') { $GLOBALS['favorite'] = false; remove_action('widget_entry_card_date_before', 'get_fav_button'); } return $output; }, 10, 2); return $out; }, 10, 3); // ウィジェットに設定メニューを追加 add_action('in_widget_form', function ($widget, $return, $instance) { // 対象ウィジェットを確認 if ($widget->id_base !== 'new_entries') { return; } $f_id = $widget->get_field_id('favorite'); $f_name = $widget->get_field_name('favorite'); $checked = checked(!empty($instance['favorite']), true, false); ?> <p> <input type="checkbox" id="<?= $f_id; ?>" class="widefat" name="<?= $f_name; ?>" value="1" <?= $checked; ?>> <label for="<?= $f_id; ?>">お気に入り</label> </p> <?php }, 10, 3); // ウィジェットの入力データを更新 add_filter('widget_update_callback', function ($instance, $new_instance) { $instance['favorite'] = isset($new_instance['favorite']) ? 1 : 0; return $instance; }, 10, 2); // ウィジェットの表示設定を変更 add_filter('widget_display_callback', function ($instance, $widget, $args) { if ($widget->id_base === 'new_entries' && !empty($instance['favorite'])) { $GLOBALS['favorite'] = true; add_action('widget_entry_card_date_before', 'get_fav_button'); } else { $GLOBALS['favorite'] = false; remove_action('widget_entry_card_date_before', 'get_fav_button'); } return $instance; }, 10, 3); - ラベル「お気に入りをリセット」ボタンを追加
以下のコードをfunctions.phpに追加します。
add_shortcode('my_clear_fav_button', function () { ob_start(); ?> <style> .clear-container { text-align: center; } /* 非活性 */ .clear-fav-btn { background-color: var(--fav-button-off); border-color: var(--fav-button-off); pointer-events: none; } /* 活性 */ .clear-fav-btn.active { background-color: var(--fav-button-on); border-color: var(--fav-button-on); cursor: pointer; pointer-events: auto; } </style> <script> jQuery(function ($) { /** * リセットボタンの状態を更新 * * @return {void} */ function syncClearButtonUI() { if (typeof $.cookie === 'undefined') { return; } // クッキーからデータを取得 const currentCook = JSON.parse($.cookie('my_fav_page_list') || '[]'); $('.clear-fav-btn').toggleClass('active', currentCook.length > 0); } $(document).on('click', '.clear-fav-btn', function (e) { e.preventDefault(); if (typeof $.removeCookie !== 'undefined') { // クッキーを削除 $.removeCookie('my_fav_page_list', { path: '/' }); } // ボタンの状態を更新 syncClearButtonUI(); // お気に入り一覧の更新を通知 $(document).trigger('favListUpdated'); }); // お気に入り一覧更新時に状態を更新 $(document).on('favListUpdated', function () { syncClearButtonUI(); }); // 初回の状態を確認 syncClearButtonUI(); }); </script> <div class="clear-container"> <button class="clear-fav-btn btn">お気に入りをリセット</button> </div> <?php return ob_get_clean(); });
