Ajaxページネーション

例えばこんなケースがある!

CPT一覧でターム別にタブ切り替えで表示したい

ここで問題なのはページネーションをどうするか?

<DEMO>

DEMOはターム毎にページネーションが独立しており、タブを切り替えてもページネーションが影響を受けることがない!


AjaxページネーションのDEMOはこちら

<解説>

通常WordPressでのページネーションはリンクで行い、URLが「wp1stop.ne/blog/page/2/」のように変更される。
CPT一覧が1つの場合は何も問題はないが、ターム別にタブ切り替えで表示する場合はURL(/page/2/)を共有することになるので思うような動作にならない。
この場合リンクではなくAjaxを使ったページネーションを実装することで解決出来る!

<実装方法>

WordPressにはajaxを扱うアクションフックが2つがある。「wp_ajax_action(+name)」「wp_ajax_nopriv_action(+name)」
PHPはこのアクションフック使ってAjaxコールバックコードを書く。
またAjaxページネーション生成用のコードも必要である。

JSはAjaxコードが必要である。


Ajaxコールバック、Ajaxページネーション生成コードをfunctions.phpなどに追加する

functions.php
//Ajax callback
add_action('wp_ajax_load_more_posts', 'load_more_posts_callback');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts_callback');
function load_more_posts_callback() {
	$post_type = $_POST['post_type'];
	$posts_per_page = $_POST['posts_per_page'];
	$paged = $_POST['page'];
	$taxonomy = $_POST['taxonomy'];
	$terms = $_POST['terms'];
	$html = "";
	$args = array(
		'post_type' => $post_type,
		'post_status' => 'publish',
		'posts_per_page' => $posts_per_page,
		'paged' => $paged,
	);
	if( $taxonomy != '' && $terms != '' ){
		$args['tax_query'] = array(
			array(
				'taxonomy' => $taxonomy,
				'field'    => 'slug',
				'terms'    => $terms,
			)
		);
	}
	$query = new WP_Query($args);
	$total_posts = $query->found_posts;
	if ($query->have_posts()) {
		while ( $query->have_posts() ) : $query->the_post();
			$term_slug = $term_name = '';
			$get_terms = get_the_terms(get_the_ID(), $taxonomy);
			if ( ! empty( $get_terms ) ) {
				if ( ! is_wp_error( $get_terms ) ) {
					$term_name = $get_terms[0]->name;
					$term_slug = $get_terms[0]->slug;
				}
			}
			$html .= '<ul class="col2blog"><li class="l">';
			$permalink = get_permalink();
			$time = get_the_time("Y/m/d");
			$title = the_title('','',false);
			$content = do_shortcode( get_the_content() );
			$url = get_post_meta(get_the_ID(), 'proresults_url', true);
			if ( has_post_thumbnail() ) {
				$html .= get_the_post_thumbnail( get_the_ID(), 'thumb300_200', array('class' => 'newsimg') );
			}else{
				$html .= '<img src="' . get_template_directory_uri(). '/img/noimg.gif" alt="No image" class="newsimg">' . PHP_EOL;
			}
			$html .= '</a></li>';
			$html .= '<li class="spa"></li>';
			$html .= '<li class="r">';
			$html .= '<p class="ti"><i class="fa fa-clock-o"></i>';
			$html .= '<a href="' . $permalink .'" class="link">';
			$html .= $time . ' ';
			if( $terms != '' ){
				$html .= '<span class="cate '.$term_slug.'">' . $term_name . '</span><br>';
			}
			$html .= $title . '</p>' . PHP_EOL;
			$html .= '<a href="' . $url . '" target="_blank">' . $url . '</a>';
			$html .= '<p class="excerpt">' . $content . '</p>' . PHP_EOL;
			$html .= '</li></ul>';
		endwhile;
		wp_reset_postdata();
		$response = array(
			'html' => $html,
			'show_button' => true,
			'total_posts' => $total_posts,
		);
	} else {
		$response = array(
			'show_button' => false,
		);
	}
	$response = json_encode($response);
	echo $response;
	wp_die();
}

//Ajaxページネーション
function ajax_pagination($max_page = 1, $posts_per_page = 3, $post_type = '', $taxonomy = '', $terms = '', $showitems = 5, $cuurent_maxpage = false){
	//$showitems = ($range * 2)+1;
	$paged = 1;
	$admin_url = admin_url() . 'admin-ajax.php';

	if(1 != $max_page && $posts_per_page != ''  && $post_type != '' ) {
		if( $taxonomy != '' && $terms != '' ) {
			echo '<div class="pagingBox" data-maxpage="'.$max_page.'" data-post_type="'.$post_type.'" data-admin_url="'.$admin_url.'" data-terms="'.$terms.'" data-taxonomy="'.$taxonomy.'" data-posts_per_page="'.$posts_per_page.'" data-showitems="'.$showitems.'" data-cuurent_maxpage="'.$cuurent_maxpage.'">';
		} else {
			echo '<div class="pagingBox" data-maxpage="'.$max_page.'" data-post_type="'.$post_type.'" data-admin_url="'.$admin_url.'" data-terms="" data-taxonomy="" data-posts_per_page="'.$posts_per_page.'" data-showitems="'.$showitems.'" data-cuurent_maxpage="'.$cuurent_maxpage.'">';
		}
		//先頭へ
		echo '<button data-page="1" class="pagingBut noNum top"><i class="fas fa-angle-double-left"></i></button>';
		//1つ戻るGo back one
		//$backOne = ($paged == 1) ? 1 : $paged;
		echo '<button data-page="" class="pagingBut noNum backOne"><i class="fas fa-angle-left"></i></button>';
		echo '<div class="numBox">';
		//番号つきページ送りボタン
		for ( $i=1; $i <= $showitems; $i++ ) {
			if ( $i <= $max_page ) {
				$class = ($paged == $i) ? ' active' : '';
				echo '<button data-page="'.$i.'" class="pagingBut'.$class.'">'.$i.'</button>';
			}
		}
		echo '</div>';
		//1つ進むadvance one step
		echo '<button data-page="2" class="pagingBut noNum oneStep"><i class="fas fa-angle-right"></i></button>';
		//最後尾へ
		echo '<button data-page="'.$max_page.'" class="pagingBut noNum end"><i class="fas fa-angle-double-right"></i></button>';
		echo '</div><!--.pagingBox-->';
		if($cuurent_maxpage) {
			echo '<div class="cuurent_maxpage"><span>1</span> / '.$max_page.'</div>';
		}
	}
}

CPT一覧ページなどにAjaxページネーションを埋め込む

//pagination
ajax_pagination( $wp_query->max_num_pages, $posts_per_page, $post_type, $taxonomy, $terms );

AjaxコードをJSファイルに

//ajax.js
jQuery(function ($) {
	$(document).on('click', '.pagingBut', function(){
		var $pagingBox = $(this).closest('.pagingBox');
		$pagingBox.find('.pagingBut').removeClass('active');
		const post_type = $pagingBox.data('post_type');
		const admin_url = $pagingBox.data('admin_url');
		let page = $(this).attr('data-page');
		let maxpage = $pagingBox.data('maxpage');
		let showitems = $pagingBox.data('showitems');
		const terms = $pagingBox.data('terms');
		const taxonomy = $pagingBox.data('taxonomy');
		const posts_per_page = $pagingBox.data('posts_per_page');
		const cuurent_maxpage = $pagingBox.data('cuurent_maxpage');
		const $thisEle = $(this);

		$.ajax({
			url: admin_url,
			type: 'POST',
			data: {
				action: 'load_more_posts',
				post_type: post_type,
				posts_per_page: posts_per_page,
				page: page,
				terms: terms,
				taxonomy: taxonomy,
				showitems: showitems,
			}, success: function(response) {
				var result = JSON.parse(response);
				$pagingBox.closest('.tab_content_description').children('.text_flex').html(result.html);
				page = Number(page);
				maxpage = Number(maxpage);
				showitems = Number(showitems)
				if (page >= 2) {$pagingBox.children('.pagingBut.backOne').attr('data-page', (page - 1));
				} else {$pagingBox.children('.pagingBut.backOne').attr('data-page', page);}
				if (page < maxpage) {$pagingBox.children('.pagingBut.oneStep').attr('data-page', (page + 1));
				} else {$pagingBox.children('.pagingBut.oneStep').attr('data-page', page);}

				//番号つきページ送りボタン
				let numBox = '';
				let classN = '';
				let startN = 0;
				let cou = 0;
				let maxdiffer = 0;
				let pagediffer = 0;
				let show = (showitems-1);//case 5 -> 4
				let show2 = ((showitems-1)/2);//case 5 -> 2
				let cuurentmaxpage = '';

				if (cuurent_maxpage) {
					cuurentmaxpage = ''+page+' / '+maxpage;
				}
				$pagingBox.closest('.tab_content_description').find('.cuurent_maxpage').html(cuurentmaxpage);

				if (maxpage <= showitems) {
					startN = 1;
				} else {
					maxdiffer = maxpage - show;
					pagediffer = page - show2;
					if (pagediffer >= maxdiffer) {
						startN = maxdiffer;
					} else if (pagediffer < 1) {
						startN = 1;
					} else {
						startN = pagediffer;
					}
				}

				for (let i = startN; i <= maxpage; i++) {
					if (cou < showitems) {
						classN = (page == i) ? ' active' : '';
						numBox += '\n';
					}
					cou++;
				}
				$pagingBox.closest('.tab_content_description').find('.numBox').html(numBox);
			}, error: function(xhr, status, error) {
				console.error(xhr);
			}
		});
	});

});

<まとめ>

WordPressでajaxの実装は簡単なのでページネーション以外にもトライしてみたいと思う。
今後、機会があればタブ切替(Ajaxページネーション付き)のプラグイン化を検討中。