「新着記事」ブロックを無限スクロール化する方法

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

はじめに

以前の記事では、ボタンをクリックする度にAjax通信を行って表示件数を動的に増やす方法を紹介しました。

今回はこの方法をさらに発展させ、スクロール操作と連動させて自動で表示件数を増やす(無限スクロール化)カスタマイズを行います。
ユーザーがボタンを押す手間を無くし、よりシームレスにコンテンツを閲覧できるようになります。

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

完成イメージ

実装手順

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

  • ラベル
    Gutenbergエディターでの設定

    「新着記事」ブロックを配置し、表示する記事の数を3とします。

  • ラベル
    PHPを追加

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

    /**
     * Ajaxコールバック関数
     *
     * @return void
     */
    if (!function_exists('update_shortcode_callback')):
    function update_shortcode_callback() {
      if (!session_id() && !headers_sent()) {
        session_start();
      }
    
      // セッションから保存済みの属性配列を取得
      $saved_atts = isset($_SESSION['new_list_atts']) && is_array($_SESSION['new_list_atts'])
        ? $_SESSION['new_list_atts']
        : array();
    
      // 追加件数を設定
      $add_count = 3;
    
      // 現在の件数を取得して加算
      $new_count = intval(isset($saved_atts['entry_count']) ? $saved_atts['entry_count'] : 0) + $add_count;
    
      // セッションの保持件数を変更
      $_SESSION['new_list_atts']['entry_count'] = $new_count;
    
      // ショートコード属性用の配列を設定
      $shortcode_atts = $saved_atts;
    
      // 属性名を変更
      $shortcode_atts['count'] = $new_count;
      unset($shortcode_atts['entry_count']);
    
      // パラメータ文字列を構築
      $attr_str = '';
    
      foreach ($shortcode_atts as $key => $value) {
        if (is_array($value)) {
          // 配列をカンマ区切り文字列へ置換
          $value = implode(',', $value);
        } elseif (is_object($value) || is_null($value)) {
          // オブジェクトまたはNULLを処理対象外
          continue;
        }
    
        $attr_str .= ' ' . sanitize_key($key) . '="' . esc_attr($value) . '"';
      }
    
      // Ajax実行時の予約記事の混入を防止
      $force_publish_status = function ($query) {
        if (wp_doing_ajax()) {
          $query->set('post_status', 'publish');
        }
      };
    
      add_action('pre_get_posts', $force_publish_status, 999);
    
      // ショートコードを実行して出力
      $shortcode = '[new_list' . $attr_str . ']';
      echo do_shortcode($shortcode);
    
      // 処理後にフィルターを解除
      remove_action('pre_get_posts', $force_publish_status, 999);
    
      wp_die();
    }
    endif;
    
    
    // 新着記事の属性を設定
    add_filter('shortcode_atts_generate_widget_entries_tag', function ($out, $pairs, $atts) {
      if (!session_id() && !headers_sent()) {
        session_start();
      }
    
      // 設定値をセッションへ記録
      if (!wp_doing_ajax()) {
        $_SESSION['new_list_atts'] = is_array($out) ? $out : array();
      }
    
      return $out;
    }, 10, 3);
    
    
    // フロント用JS・CSSを追加
    add_action('wp_enqueue_scripts', function () {
      $url = admin_url('admin-ajax.php');
    
      ob_start();
      ?>
      jQuery(function ($) {
    
        // 新着記事一覧の要素を取得
        var container = $('.new-list-box');
    
        // 新着記事一覧が存在しない場合は処理を終了
        if (!container.length) {
          return;
        }
    
        // Ajax通信中かどうかを設定
        var isLoading = false;
    
        // これ以上読み込む記事がないかどうかを設定
        var finished = false;
    
        // 読み込みを開始する画面上端からの距離を設定
        var triggerOffset = 200;
    
        // 次回の読み込みを許可する状態を設定
        var canTrigger = true;
    
        // スクロール処理の実行待ち状態を設定
        var ticking = false;
    
    
        /**
         * 追加データの読み込みを実行
         *
         * @returns {void}
         */
        function loadMore() {
    
          // Ajax通信中、読み込み完了済み、または再発火禁止中の場合は処理を終了
          if (isLoading || finished || !canTrigger) {
            return;
          }
    
          // 次回の読み込みを禁止
          canTrigger = false;
    
          // Ajax通信中の状態を設定
          isLoading = true;
    
          $.ajax({
            type: 'POST',
            url: '<?php echo esc_url($url); ?>',
            data: {
              action: 'update_shortcode'
            },
            success: function (response) {
    
              // 取得結果が空かどうかを確認
              if ($.trim(response) === '') {
    
                // これ以上データが無いため処理を終了
                finished = true;
    
              } else {
    
                // 新着記事一覧を取得結果へ置換
                container.html(response);
              }
            },
    
            // Ajax通信完了後の処理を設定
            complete: function () {
    
              // Ajax通信中の状態を解除
              isLoading = false;
    
              // Ajax完了後の位置を確認
              checkPosition();
            }
          });
        }
    
        /**
         * リストの位置を確認
         *
         * @returns {void}
         */
        function checkPosition() {
    
          // 読み込み完了済みの場合は処理を終了
          if (finished) {
            return;
          }
    
          // 新着記事一覧の位置とサイズを取得
          var rect = container[0].getBoundingClientRect();
    
          // 最下部が200pxより下側に戻ったことを確認
          if (rect.bottom > triggerOffset) {
    
            // 次回の読み込みを許可
            canTrigger = true;
          }
    
          // Ajax通信中または次回の読み込みが禁止されている場合は処理を終了
          if (isLoading || !canTrigger) {
            return;
          }
    
          // 新着記事一覧の最下部が画面上端から200px以内か確認
          if (rect.bottom <= triggerOffset) {
    
            // 追加データの読み込みを実行
            loadMore();
          }
        }
    
        /**
         * スクロールイベントを処理
         *
         * @returns {void}
         */
        function onScroll() {
    
          // 前回のスクロール処理が実行待ちの場合は処理を終了
          if (ticking) {
            return;
          }
    
          // スクロール処理の実行待ち状態を設定
          ticking = true;
    
          // 次の描画タイミングで位置を確認
          window.requestAnimationFrame(function () {
    
            // 新着記事一覧の位置を確認
            checkPosition();
    
            // スクロール処理の実行待ち状態を解除
            ticking = false;
          });
        }
    
    
        // スクロールイベントを登録
        $(window).on('scroll', onScroll);
    
    
        // 初期表示時にも位置を確認
        checkPosition();
      });
      <?php
      wp_add_inline_script('jquery', ob_get_clean(), 'after');
    });
    
    
    // Ajaxアクションを追加
    add_action('wp_ajax_update_shortcode', 'update_shortcode_callback');
    add_action('wp_ajax_nopriv_update_shortcode', 'update_shortcode_callback');

さいごに

今回紹介した方法を使えば、「新着記事」ブロックにシームレスな無限スクロール機能を簡単に導入できます。
ユーザーがボタンをクリックする手間を抑え、ストレスのないスムーズな記事閲覧体験を提供できるようになりますので、サイトの回遊率アップや利便性向上にぜひ役立ててみてください。

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