固定ページの一覧をナビゲーションとしてサイドバーなどに設置する時に、
ページに設定した親ページに基づいてネストされたリスト(ul
)で出力したいことがあると思います。
その際には、以下の項目が必要となります。
- ネストされたリスト(
ul
)で出力されるようにする。 - 表示したくない固定ページを除外できるようにする。
- 現在表示中の固定ページがわかるようにする。
- CSSで装飾できるように HTML要素に class属性 をたくさん付ける。
- リストは、開閉式にする。(現在表示中のページを含む
ul
のみ開けておく)
WordPress のカスタムメニュー(register_nav_menu()
とwp_nav_menu()
)でも実装できますが、
カスタマイズにWalker_Nav_Menu
クラスを extends したクラスを新しく作ったり、
子ページのメニュー登録は管理画面で毎回操作しなくちゃいけなかったりと、案外メンドウなので、
WPテーマをいじる人は「カスタマイズが楽」で、
記事を書く人は「公開したら勝手に表示される」ようなものとして使えればと思います。
以下は、もっと良い方法が きっとあると思うけど、G○○gle先生に聞いても出てこなかったので、
上の項目を満たす文字列を返す関数を考えてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
<?php global $wp_page_list_depth; // 固定ページ階層の深さを格納する配列 $wp_page_list_depth = array(); // 固定ページIDを渡すと子孫ページを含めたリストを返す関数 function print_page_list( $page_id = '', $exclude_page_ids = '' ) { // 現在表示中の固定ページのIDを取得 global $wp_query; $display_page_id = ( is_page() ) ? get_the_ID() : 0; // 回帰的にこの関数を呼び出して子孫ページを含めたリストを整形 if ( isset($page_id) && !empty($page_id) && is_array($page_id) ) { // 引数'$page_id'が配列の時の処理 $result = ''; // 返り値の文字列を入れる変数 // 分解して回帰処理 foreach ( $page_id as $key => $value ) { $result .= print_page_list( $value, $exclude_page_ids ); } // 回帰処理で取得した文字列を返す return $result; } elseif ( isset($page_id) && !empty($page_id) ) { // 引数'$page_id'がある時の処理 $result = ''; // 返り値の文字列を入れる変数 // このページの情報を取得 $the_page = get_post($page_id); if ( $the_page ) { // このページの情報を整形 $post_id = $the_page->ID; $post_id_class = ' page-id-'.$post_id.' '; $this_display_page_class = ( $post_id == $display_page_id ) ? ' this-display-page ' : ''; $post_parent = ( $the_page->post_parent ) ? $the_page->post_parent : false; $post_parent_class = ( $post_parent ) ? ' parent-page-id-'.$post_parent.' ' : ' top-level-page '; global $wp_page_list_depth; if ( $post_parent ) { $wp_page_list_depth[$post_id] = $wp_page_list_depth[$post_parent] + 1; } else { $wp_page_list_depth[$post_id] = 0; } $page_depth_class = ' depth-'.$wp_page_list_depth[$post_id].' '; $post_title = esc_attr( apply_filters( 'the_title', $the_page->post_title ) ); $permalink = esc_url( apply_filters( 'the_permalink', get_permalink($post_id) ) ); // 子ページの情報を取得 $args = array( 'sort_order' => 'ASC', 'sort_column' => 'menu_order', 'hierarchical' => 0, // 'parent' を使用するため 0 (false) を指定 'exclude' => $exclude_page_ids, 'parent' => $post_id, // 'parent' => $post_id, で子ページを取得 'post_type' => 'page', 'post_status' => 'publish' ); $child_pages = get_pages( $args ); $bottom_page_class = ( $child_pages ) ? ' has-child-page ' : ' not-has-child-page '; // 子孫ページの情報を取得 $has_dispray_page_class = ''; if ( $child_pages ) { // 子孫ページの情報 $args = array( 'sort_order' => 'ASC', 'sort_column' => 'menu_order', 'hierarchical' => 1, 'exclude' => $exclude_page_ids, 'child_of' => $post_id, // 'child_of' => $post_id, で子孫ページを取得 'parent' => -1, // 'parent' => -1, で全てのページを取得 'post_type' => 'page', 'post_status' => 'publish' ); $descendants_pages = get_pages( $args ); if ( $descendants_pages ) { foreach ( $descendants_pages as $descendants_page ) { if ( $descendants_page->ID == $display_page_id ) { $has_dispray_page_class = ' has-display-page '; } } } } // リストを整形 $result .= '<li class="'; $result .= $post_parent_class; $result .= $post_id_class; $result .= $page_depth_class; $result .= $bottom_page_class; $result .= $has_dispray_page_class; $result .= $this_display_page_class; $result .= '">'; if ( $child_pages ) { $result .= '<span class="has-child-tab"> </span>'; } else { $result .= '<span class="not-has-child-tab"> </span>'; } $result .= '<a href="'.$permalink.'" title="'.$post_title.'">'.$post_title.'</a>'; if ( $child_pages ) { $result .= "\n".'<ul>'."\n"; foreach ( $child_pages as $child_page ) { // 回帰的にこの関数を呼び出して子孫ページを含めたリストを取得 $result .= print_page_list( $child_page->ID, $exclude_page_ids ); } $result .= '</ul>'."\n"; } $result .= '</li>'."\n"; } // リストの文字列を返す return $result; } return ''; } // 固定ページ一覧の階層化されたリストを返す関数 function wp_page_list( $exclude_page_ids = '' ) { // トップレベルの固定ページをループしてIDから子孫ページを含めたリストを整形 $args = array( 'sort_order' => 'ASC', 'sort_column' => 'menu_order', 'hierarchical' => 1, 'exclude' => $exclude_page_ids, 'parent' => 0, // 'parent' => 0, でトップレベルのページを取得 'post_type' => 'page', 'post_status' => 'publish' ); $pages = get_pages( $args ); $output = ''; if ( $pages ) { $output .= '<ul class="wp-page-list">'."\n"; foreach ( $pages as $page ) { $output .= print_page_list( $page->ID, $exclude_page_ids); } $output .= '</ul>'."\n"; } return $output; } ?> |
上記のコードを functions.php に追加して、
以下のコードをテンプレートの任意の場所に貼り付ければ、固定ページの一覧が出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php // 固定ページ一覧を出力 if ( function_exists('wp_page_list') ) { echo wp_page_list(); } // 特定の固定ページをリストから除く場合は、 // 固定ページの ID をコンマ区切りの文字列で引数に指定 $exclude_page_ids = '71,73'; if ( function_exists('wp_page_list') ) { echo wp_page_list($exclude_page_ids); } ?> |
下は、リスト開閉の jQuery です。
こちらも functions.php に貼り付ければ、動作します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?php // script要素を出力する関数を head で実行するよう登録 add_action( 'wp_head', 'wp_page_list_script'); // 公開エリア // script要素を出力する関数 function wp_page_list_script() { ?> <script id="wp-page-list-script" type="text/javascript"> jQuery(function($){ // 全てを閉じる $('.wp-page-list li span.has-child-tab').addClass('close'); $('.wp-page-list li ul').addClass('close').hide(); // 表示中のページまで開ける $('.wp-page-list li.has-display-page > span.has-child-tab').removeClass('close').addClass('open'); $('.wp-page-list li.has-display-page > ul').show().removeClass('close').addClass('open'); // クリックイベント $('.has-child-tab').on('click', function(){ // 開閉とクラスの除去と追加 if ( $(this).hasClass('open') ) { $(this).removeClass('open').addClass('close'); $(this).siblings('ul').removeClass('open').addClass('close').slideUp('fast'); } else if ( $(this).hasClass('close') ) { $(this).removeClass('close').addClass('open'); $(this).siblings('ul').slideDown('fast').removeClass('close').addClass('open'); } }); }); </script> <?php return; } ?> |
下は、 CSS です。(例として)
こちらは functions.php に書かずに、直接スタイルシートに書いた方がいいですね。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?php // style要素を出力する関数を head で実行するよう登録 add_action( 'wp_head', 'wp_page_list_style'); // 公開エリア // style要素を出力する関数 function wp_page_list_style() { ?> <style id="wp-page-list-style" type="text/css"> .wp-page-list ul { padding-left: 20px; } .wp-page-list .this-display-page { font-weight: bold; } .wp-page-list .not-has-child-page-tab, .wp-page-list .has-child-tab.open, .wp-page-list .has-child-tab.close { display: inline-block; width: 2em; text-align: center; } .wp-page-list .not-has-child-tab {} .wp-page-list .has-child-tab.open {} .wp-page-list .has-child-tab.close {} .wp-page-list .not-has-child-page-tab:after { display: inline-block; content: "・"; cursor: default; } .wp-page-list .has-child-tab.open:after { display: inline-block; content: "─"; cursor: pointer; } .wp-page-list .has-child-tab.close:after { display: inline-block; content: "┼"; cursor: pointer; } </style> <?php return; } ?> |
これで、画像のようなリストが出力できます。
出力されるHTMLは、下のようになります。
(見やすいように改行とインデントをつけてます)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<ul class="wp-page-list"> <li class=" top-level-page page-id-71 depth-0 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/" title="トップページ">トップページ</a> </li> <li class=" top-level-page page-id-73 depth-0 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/blog" title="記事一覧">記事一覧</a> </li> <li class=" top-level-page page-id-199 depth-0 has-child-page "> <span class="has-child-tab"> </span> <a href="http://if1.tech/wordpress-%e9%96%a2%e9%80%a3" title="WordPress 関連">WordPress 関連</a> <ul> <li class=" parent-page-id-199 page-id-101 depth-1 has-child-page "> <span class="has-child-tab"> </span> <a href="http://if1.tech/wordpress-%e9%96%a2%e9%80%a3/wp-font-awesome" title="WordPress Plug-in ‘WP Font Awesome’">WordPress Plug-in ‘WP Font Awesome’</a> <ul> <li class=" parent-page-id-101 page-id-130 depth-2 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/wordpress-%e9%96%a2%e9%80%a3/wp-font-awesome/thousand-column" title="WordPress Plug-in ‘1000 Column’">WordPress Plug-in ‘1000 Column’</a> </li> </ul> </li> </ul> </li> <li class=" top-level-page page-id-201 depth-0 has-child-page "> <span class="has-child-tab"> </span> <a href="http://if1.tech/css-%e9%96%a2%e9%80%a3" title="CSS 関連">CSS 関連</a> <ul> <li class=" parent-page-id-201 page-id-107 depth-1 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/css-%e9%96%a2%e9%80%a3/g-css-border-radius" title="CSS Generator ‘Border Radius’">CSS Generator ‘Border Radius’</a> </li> </ul> </li> <li class=" top-level-page page-id-92 depth-0 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/inquiry" title="お問い合わせ">お問い合わせ</a> </li> <li class=" top-level-page page-id-94 depth-0 not-has-child-page "> <span class="not-has-child-page-tab"> </span> <a href="http://if1.tech/terms" title="サイトのご利用について">サイトのご利用について</a> </li> </ul> |
li要素に付加されるクラスは次のとおりです。
- 階層は、
depth-?
で表される。?
に階層番号。一番上は0
。 - 一番上の階層は、
top-level-page
とdepth-0
がつく。 - 子ページを持っていれば、
has-child-page
がつく。 - 一番下の階層(子ページを持っていない)は、
not-has-child-page
がつく。 - 親ページがある場合、親ページのIDを表す
parent-page-id-??
がつく。??
に親ページのID。 - ページIDは、
page-id-??
で表される。??
にページID。 - 固定ページが表示中なら、
this-display-page
がつく。 - 表示中の固定ページを含んでいれば、
has-display-page
がつく。 - 子ページを持っていれば、
<span class="has-child-tab"> </span>
が入る。 - 子ページを持っていなければ、
<span class="not-has-child-tab"> </span>
が入る。
再帰処理の練習がてら作ったので、もっと簡単な方法があるかもです。ご了承ください( ´_ゝ`)