WordPress 目次ウィジェット

以前、作成した目次ショートコードにて「サイドバー(ウィジェット)に目次を表示したい」との要望がありましたでの、実装方法をいくつか紹介したいと思います。

テキスト ウィジェットでショートコード

まずは、テキスト ウィジェットでショートコードを使用できるようにする方法です。

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

add_filter( 'widget_text', 'do_shortcode' );

これで、テキスト ウィジェットの本文のショートコードが展開されるようになります。

この設定はテーマによっては既に適用されている(またはオプションで設定できる)場合もありますが、複数記述しても動作に支障はないので記述しておいた方が確実です。

あとは、テキスト ウィジェットを追加して、本文に目次ショートコード([toc])を記述するだけです。

なお、この方法ではテキスト ウィジェットなのでウィジェットを配置したすべてのページで目次ショートコードが展開されてしまいます。表示するページを指定するにはウィジェットの表示条件を設定できるプラグイン等を使用する必要があります。

目次ウィジェット

ここでは、目次専用のウィジェットを作成します。

前記の方法では、テキスト ウィジェットなので表示ページを指定できませんでした。このウィジェットでは指定の投稿タイプの個別(シングル)ページに限定して表示可能です。

下記コードを functions.php に記述してください。

if ( ! class_exists( 'Toc_Widget' ) ) :

	/**
	 * 目次ウィジェットです。
	 *
	 * @version 1.0.0
	 * @see WP_Widget
	 */
	class Toc_Widget extends WP_Widget {

		/**
		 * 新しい目次ウィジェットのインスタンスを初期化します。
		 */
		public function __construct() {
			$widget_ops = array( 'classname' => 'widget_toc', 'description' => '目次' );
			parent::__construct( 'toc', '目次', $widget_ops );
			$this->alt_option_name = 'widget_toc';
		}

		/**
		 * ウィジェットを出力します。
		 *
		 * @see WP_Widget::widget()
		 *
		 * @param array $args ウィジェットの引数。
		 * @param array $instance オプション値。
		 */
		public function widget( $args, $instance ) {

			$enabled_post_types = isset( $instance['post_types'] ) ? $instance['post_types'] : array( 'post' );
			if ( ! is_singular( $enabled_post_types ) )
				return;

			$title = $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];
			$toc_title = isset( $instance['toc_title'] ) ? $instance['toc_title'] : '';
			$toggle_mode = isset( $instance['toggle'] ) ? absint( $instance['toggle'] ) : 0;
			$toggle = ( $toggle_mode !== 0 );
			$close = ( $toggle_mode === 2 );
			$show_count = isset( $instance['show_count'] ) ? absint( $instance['show_count'] ) : 0;
			$depth = isset( $instance['depth'] ) ? absint( $instance['depth'] ) : 2;
			$top_level = isset( $instance['top_level'] ) ? absint( $instance['top_level'] ) : 2;

			$shortcode = sprintf( '[toc title="%s" toggle="%s" close="%s" showcount=%s depth=%s toplevel=%s]',
				$toc_title,
				$toggle ? 'true' : 'false',
				$close ? 'true' : 'false',
				$show_count,
				$depth,
				$top_level
			 );

			$toc = do_shortcode( $shortcode );
			if ( $toc ) {
				echo $args['before_widget'] . $title . $toc . $args['after_widget'];
			}
		}

		/**
		 * ウィジェットの設定フォームを出力します。
		 *
		 * @see WP_Widget::form()
		 *
		 * @param array $instance オプション値。
		 */
		public function form( $instance ) {
			$title = isset( $instance['title'] ) ? $instance['title'] : '';
			$enabled_post_types = isset( $instance['post_types'] ) ? $instance['post_types'] : array( 'post' );
			$toc_title = isset( $instance['toc_title'] ) ? $instance['toc_title'] : '';
			$toggle = isset( $instance['toggle'] ) ? absint( $instance['toggle'] ) : 0;
			$show_count = isset( $instance['show_count'] ) ? absint( $instance['show_count'] ) : 2;
			$depth = isset( $instance['depth'] ) ? absint( $instance['depth'] ) : 2;
			$top_level = isset( $instance['top_level'] ) ? absint( $instance['top_level'] ) : 2;

			echo '<p><label for="' . $this->get_field_id( 'title' ) . '">' . __( 'Title:' ) . '</label>';
			echo '<input class="widefat" id="' . $this->get_field_id( 'title' ) . '" name="' . $this->get_field_name( 'title' ) . '" type="text" value="' . esc_attr( $title ) . '" /></p>';

			$post_types = get_post_types( array( 'public' => true ), 'object' );
			unset( $post_types['attachment'], $post_types['revision'], $post_types['nav_menu_item'] );
			echo '<span>' . __( '投稿タイプ:' ) . '</span>';
			echo '<div style="margin: 2px 0 1em; padding: 0 5px; min-height: 42px; max-height: 100px; border: 1px solid #ddd; overflow-y: scroll;">';
			echo '<ul style="margin: 5px 0;">';
			foreach ( $post_types as $post_type ) {
				echo '<li><label class="selectit">';
				printf( '<input type="checkbox" value="%s" id="%s" name="%s" %s/>%s (%s)',
					$post_type->name,
					$this->get_field_id( 'post_types[]' ),
					$this->get_field_name( 'post_types[]' ),
					checked( in_array( $post_type->name, $enabled_post_types ), true, false ),
					$post_type->label,
					$post_type->name
				);
				echo '</label></li>';
			}
			echo '</ul>';
			echo "</div>\n";
			
			echo '<p><label for="' . $this->get_field_id( 'toc_title' ) . '">' . __( '目次タイトル:' ) . '</label>';
			echo '<input class="widefat" id="' . $this->get_field_id( 'toc_title' ) . '" name="' . $this->get_field_name( 'toc_title' ) . '" type="text" value="' . esc_attr( $toc_title ) . '" /></p>';

			printf( '<p><label for="%1$s">%2$s</label> <select class="tiny-text" id="%1$s" name="%3$s">', $this->get_field_id( 'toggle' ), __( '開閉リンク:' ), $this->get_field_name( 'toggle' ) );
			printf( '<option value="%s"%s>%s</option>', 0, selected( 0, $toggle, false ), '表示しない' );
			printf( '<option value="%s"%s>%s</option>', 1, selected( 1, $toggle, false ), '開いた状態で表示' );
			printf( '<option value="%s"%s>%s</option>', 2, selected( 2, $toggle, false ), '閉じた状態で表示' );
			echo '</select></p>';

			echo '<p><label for="' . $this->get_field_id( 'show_count' ) . '">' . __( '目次を表示する見出し項目の数:' ) . '</label> ';
			echo '<input class="tiny-text" id="' . $this->get_field_id( 'show_count' ) . '" name="' . $this->get_field_name( 'show_count' ) . '" type="number" step="1" min="0" value="' . $show_count . '" size="3" /></p>';

			echo '<p><label for="' . $this->get_field_id( 'depth' ) . '">' . __( '出力する階層数:' ) . '</label> ';
			echo '<input class="tiny-text" id="' . $this->get_field_id( 'depth' ) . '" name="' . $this->get_field_name( 'depth' ) . '" type="number" step="1" min="1" max="6" value="' . $depth . '" size="3" /></p>';

			printf( '<p><label for="%1$s">%2$s</label> <select class="tiny-text" id="%1$s" name="%3$s">', $this->get_field_id( 'top_level' ), __( 'トップの階層のヘッダー:' ), $this->get_field_name( 'top_level' ) );
			for ( $i = 1; $i <= 6; $i++ ) {
				printf( '<option value="%s"%s>%s</option>',
					$i,
					selected( $i, $top_level, false ),
					'見出し' . $i
				);
			}
			echo '</select></p>';
		}

		/**
		 * ウィジェットの設定を更新します。
		 *
		 * @see WP_Widget::update()
		 *
		 * @param array $new_instance 新しいオプション。
		 * @param array $old_instance 以前のオプション。
		 */
		public function update( $new_instance, $old_instance ) {
			$instance = array();
			$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
			$instance['post_types'] = isset( $new_instance['post_types'] ) ? $new_instance['post_types'] : array();
			$instance['toc_title'] = ( ! empty( $new_instance['toc_title'] ) ) ? strip_tags( $new_instance['toc_title'] ) : '';
			$instance['toggle'] = ( ! empty( $new_instance['toggle'] ) ) ? absint( $new_instance['toggle'] ) : 0;
			$instance['show_count'] = ( ! empty( $new_instance['show_count'] ) ) ? absint( $new_instance['show_count'] ) : 2;
			$instance['depth'] = ( ! empty( $new_instance['depth'] ) ) ? absint( $new_instance['depth'] ) : 2;
			$instance['top_level'] = ( ! empty( $new_instance['top_level'] ) ) ? absint( $new_instance['top_level'] ) : 2;

			return $instance;
		}
	}

	add_action( 'widgets_init', function() { register_widget( 'Toc_Widget' ); } );

endif;

上記コードには、目次ショートコードのコードは含まれていません。別途、目次ショートコードのコード(バージョン1.1.0 以上)を追加してください。

スタイル(CSS)

スタイル(CSS)の例です。テーマの style.css 等に記述してください。

/*
TOC Widget
*/
.widget_toc .toc {
    width: 100%;
    padding: 0;
    border: 0;
    background-color: transparent;
}

コメントを残す

メールアドレスが公開されることはありません。

日本語でコメントを入力してください。(スパム対策)