プラグインなしで「お気に入り」機能を追加する方法(ローカルストレージ版)

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

はじめに

以前の記事では、ブラウザのクッキーを利用してプラグインなしでお気に入り機能を実装する方法を紹介しました。

今回は、クッキーの代わりにブラウザのローカルストレージを利用「お気に入り」機能を追加します。

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

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

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

ローカルストレージによるデータ保存の仕組み

有効期限のない「ローカルストレージ」にデータを保存することで、クッキーのように期間経過でデータが消えてしまうのを防止します。

ローカルストレージはPHP(サーバー)から直接読み取ることができないため、今回の実装では、ページ表示時やボタンクリック時にJavaScriptを使ってクッキーへデータを自動で同期させています。

これにより、Ajax通信を使わなくてもサーバー側で確実にお気に入りIDを受け取り、スムーズな表示を実現しています。

完成イメージ

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

使用方法

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

[my_clear_fav_button]

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

実装手順

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

  • ラベル
    PHPを追加

    以下のコードをfunctions.phpに追加します。

    // フロント用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');
        }
    
        /**
         * ローカルストレージからIDリストを取得
         *
         * @return {Array<string|number>} 保存されているIDの配列
         */
        function getIdsFromLS() {
          try {
            return JSON.parse(localStorage.getItem(KEY) || '[]');
          } catch (e) {
            return [];
          }
        }
    
        /**
         * ローカルストレージに保存し、PHP用にクッキーへもコピー
         *
         * @param {Array<string>} ids 保存するIDの配列
         * @return {void}
         */
        function saveIds(ids) {
          localStorage.setItem(KEY, JSON.stringify(ids));
          $.cookie(KEY, JSON.stringify(ids), { expires: 365, path: '/' });
        }
    
        /**
         * お気に入りボタンの状態を更新
         *
         * @return {void}
         */
        function syncUI() {
          const currentIds = getIdsFromLS().map(String);
    
          $('.fav-button').each(function () {
            const id = String($(this).data('id'));
            $(this).toggleClass('active', currentIds.indexOf(id) > -1);
          });
        }
    
        // ローカルストレージのIDとクッキーの値を取得
        var ids       = getIdsFromLS();
        var cookieVal = $.cookie(KEY) || '';
    
        // 差異がある場合はクッキーを変更
        if (cookieVal !== JSON.stringify(ids) && !(ids.length === 0 && cookieVal === '')) {
          saveIds(ids);
    
          // 未リロードの場合のみリロード済みフラグを設定してリロード
          if (!sessionStorage.getItem('fav_reloaded')) {
            sessionStorage.setItem('fav_reloaded', '1');
            location.reload();
            return;
          }
        }
        sessionStorage.removeItem('fav_reloaded');
    
        $(document).on('click', '.fav-button', function (event) {
          event.preventDefault();
          event.stopPropagation();
    
          var id  = String($(this).data('id'));
          var ids = getIdsFromLS();
          var i   = ids.indexOf(id);
    
          // お気に入り状態を変更
          if (i > -1) {
            ids.splice(i, 1);
          } else {
            ids.push(id);
          }
    
          // ローカルストレージを更新
          saveIds(ids);
    
          // スクロール位置を一時保存してリロード
          sessionStorage.setItem('fav_scroll_pos', $(window).scrollTop());
          location.href = location.pathname + '?fav=' + Date.now();
        });
    
        syncUI();
      });
      <?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']), true);
      }
    
      // お気に入り記事がない場合は0件にする
      if (empty($ids) || !is_array($ids)) {
        $args['post__in']       = [0];
        $args['posts_per_page'] = 0;
      } else {
        $ids = array_unique(array_map('intval', $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 ($) {
        var KEY = 'my_fav_page_list';
    
        /**
         * ローカルストレージからIDリストを取得
         *
         * @return {Array<string>} 保存されているIDの配列
         */
        function getIdsFromLS() {
          try {
            var v = JSON.parse(localStorage.getItem(KEY) || '[]');
            return Array.isArray(v) ? v.map(String) : [];
          } catch (e) {
            return [];
          }
        }
    
        // お気に入りボタンの状態を設定
        $('.clear-fav-btn').toggleClass('active', getIdsFromLS().length > 0);
    
        $(document).on('click', '.clear-fav-btn', function () {
          // ローカルストレージを削除
          localStorage.removeItem(KEY);
    
          // クッキーを削除
          $.removeCookie(KEY, { 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をコピーしました