プラグインなしで「お気に入り」機能を追加する方法(クッキー版)

Cocoonカスタマイズ集
この記事は約35分で読めます。
記事内に広告が含まれています。

はじめに

プラグイン「Favorites」などを使わずに、ブラウザのクッキーを利用した「お気に入り」機能を追加します。

「新着記事」ウィジェットに「お気に入り」オプションを追加し、ユーザーが選んだお気に入り投稿を表示できるようにします。
さらに、エントリーカードや投稿タイトルに「お気に入り」ボタンを設置し、クリックするとクッキーを書き換え、ウィジェットに反映させます。

また、個別に投稿を解除する手間を省くため、お気に入りに登録した投稿を一括でリセットできるボタンもあわせて追加します。

この記事では、この方法について説明します。

クッキーに関する注意

ブラウザに保存されるクッキーの有効期限や仕様上、以下のケースにお気に入りリストがリセットされますので注意ください。

  • 1年間一度もサイトへのアクセスがなかった場合
  • ブラウザのキャッシュやクッキーを消去した場合

キャッシュプラグイン使用時にお気に入り一覧が更新されない問題

以下のキャッシュ対策として、URLにタイムスタンプを付与してリロードするようにコードを修正しました。

問題点と原因、対策を述べて頂き、誠にありがとうございます。

問題と原因

キャッシュ系プラグインを使用している場合は、次の点に注意が必要です。

「お気に入り」ボタンをクリックして投稿ページが記憶されても、URL自体は変更されません。
このため、キャッシュプラグインが以前の情報を保存したままとなり、ページがキャッシュされて表示されることがあります。
その結果、「お気に入り一覧」が正しく更新されません。

対策

WP Super Cashe

キャッシュ系プラグインの設定を見直すことで解決できます。
ここではプラグイン「WP Super Cache」を例に説明します。

  1. 「WP Super Cache」の設定画面に移動します。
  2. 「高度な設定」タブを開きます。
  3. 「除外するCookie」の項目にfav_pageを追加します(図3参照)。

これにより、fav_pageに関連するCookieがキャッシュの影響を受けないようにすることができます。
結果として、「お気に入り」ボタンをクリックした後、ページが最新の状態で表示されるようになります。

完成イメージ

新着記事
「お気に入りをリセット」ボタン
ウィジェット

使用方法

「お気に入りをリセット」ボタンを表示するには、以下のショートコードを使用します。

[my_clear_fav_button]

ページリロードで表示を更新する方法

お気に入りボタンを押した瞬間にクッキーを書き換え、location.hrefを使ってページを再読み込みします。

その際、スクロール位置をsessionStorageに一時保存することで、リロード後も元の位置に自動で戻る工夫を施しています。

実装手順

以下の手順で実装します。

  • ラベル
    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);
    
      ob_start();
      ?>
      jQuery(function ($) {
        var KEY = 'my_fav_page_list';
    
        // スクロール位置を復元
        var scrollPos = sessionStorage.getItem('fav_scroll_pos');
    
        if (scrollPos) {
          $(window).scrollTop(scrollPos);
          sessionStorage.removeItem('fav_scroll_pos');
        }
    
        $(document).on('click', '.fav-button', function (e) {
          var $btn = $(this);
          var id   = $btn.data('id');
    
          e.stopPropagation();
          e.preventDefault();
    
          // スクロール位置を一時保存
          sessionStorage.setItem('fav_scroll_pos', $(window).scrollTop());
    
          // クッキーからデータを取得
          var cook  = JSON.parse($.cookie(KEY) || '[]');
          var index = cook.indexOf(id);
    
          // お気に入り状態を変更
          if (index > -1) {
            cook.splice(index, 1);
          } else {
            cook.push(id);
          }
    
          // クッキーを更新
          $.cookie(KEY, JSON.stringify(cook), { expires: 365, path: '/' });
    
          // ページをリロード
          location.href = location.pathname + '?fav=' + Date.now();
        });
    
        // クッキーの有効期限を更新
        var currentCook = JSON.parse($.cookie(KEY) || '[]');
    
        if (currentCook.length > 0) {
          $.cookie(KEY, JSON.stringify(currentCook), { expires: 365, path: '/' });
        }
    
        /**
         * クッキーからお気に入り状態を反映
         *
         * @return {void}
         */
        function check_all_cookies() {
          var currentCook = JSON.parse($.cookie(KEY) || '[]');
    
          $('.fav-button').each(function () {
            var id = $(this).data('id');
    
            if (currentCook.indexOf(id) > -1) {
              $(this).addClass('active');
            } else {
              $(this).removeClass('active');
            }
          });
        }
    
        check_all_cookies();
      });
      <?php
      wp_add_inline_script('jquery-cookie', ob_get_clean(), 'after');
    });
    
    
    // 新着記事のクエリを設定
    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']));
      }
    
      // お気に入り記事がない場合は終了
      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() {
      ?>
      <span class="fav-button" data-id="<?= get_the_ID(); ?>" title="お気に入り"><i class="fas fa-paperclip"></i></span>
      <?php
    }
    endif;
    
    
    // ウィジェットに設定メニューを追加
    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);
  • ラベル
    CSSを追加

    以下のコードをstyle.cssに追加します。

    :root {
      --fav-button-on : #ff0000;
      --fav-button-off: #ccc;
    }
    
    /* ボタン */
    .fav-button {
      color:var(--fav-button-off);
      cursor: pointer;
    }
    
    /* ボタン活性化 */
    .fav-button.active {
      color: var(--fav-button-on);
    }
    
    /* エントリーカード */
    .entry-card-title .fav-button {
      margin-right: 0.5em;
    }
    
    /* 新着記事 */
    .new-entry-cards .card-content {
      position: relative;
    }
    
    .new-entry-cards .fav-button {
      position: absolute;
      top: 0;
    }
    
    .new-entry-cards:has(.fav-button) .card-title {
      text-indent: 1.5em;
    }
    
    .new-entry-cards.large-thumb .fav-button {
      top: 0.4em;
    }
    
    .new-entry-cards.large-thumb-on .fav-button {
      top: 0.6em;
    }
  • ラベル
    「お気に入りをリセット」ボタンを追加

    以下のコードを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 ($) {
    
        // お気に入りボタンの状態を設定
        if ($.cookie('my_fav_page_list') && JSON.parse($.cookie('my_fav_page_list')).length > 0) {
          $('.clear-fav-btn').addClass('active');
        } else {
          $('.clear-fav-btn').removeClass('active');
        }
    
        $(document).on('click', '.clear-fav-btn', function () {
          // クッキーを削除
          $.removeCookie('my_fav_page_list', { path: '/' });
    
          // お気に入りボタンの状態を変更
          $(this).removeClass('active');
    
          // ページを再読み込み
          location.href = location.pathname + '?fav=' + Date.now();
        });
      });
      </script>
    
      <div class="clear-container">
        <button class="clear-fav-btn btn">お気に入りをリセット</button>
      </div>
      <?php
      return ob_get_clean();
    });
タイトルとURLをコピーしました