NeGiMeMo.net

ねぎさんのメモ帳。日常・メモ・ときどきWordPress。

開閉式のツリー構造を持つサイドバー用のカスタムウィジェットを作る

bell賞味期限切れコンテンツ

この記事は公開または最終更新から1002日くらい経過しています。
このメッセージが表示されている記事(特にプログラミング系)は情報が古くなっている可能性があるので注意して下さい。

階層を持つナビゲーションを作る際に、親メニューにマウスを載せて開閉というのはみかけますが、クリックで開閉させたい場合、親メニューを潰してしまうことになります。

そこで、Windowsのツリー表示みたいな感じで横に「+」「-」アイコンをおいてそこをクリックしたら下の階層を開くようなウィジェットを作ってみます。

本当は jQuery とかでなんとかしてみたかったのですが、 jQuery で後から追加した要素にごにょごにょするのは地味に面倒な上に自分の知識レベルだと小回りの効いたスクリプトがかけないのでそこで悩んでる間にウィジェットつくっちゃえ、とまぁそんなかんじです。

やりたいこと

要はこういうのがつくりたいんです。

 

  • メニューの横にツリーを開閉する「+」「-」ボタン
  • 現在のページがメニューの下の階層の方に存在する場合はそのメニューを開いておく
  • 一番下の階層(子を持たないメニュー)は「+」でも「-」でもないボタン

画像でいうならば、こんなかんじでしょうか?

custom-sidebar-tree-menu

では次に実現するためには、どうすればいいのか、自動的にやってほしいことをやって欲しい順に書き出してみます。

  1. メニューが子を持っているかチェックする
  2. 子を持っている場合は「+」アイコンを、持っていない場合は横向き三角なり「→」なりのアイコンを表示させる
  3. アイコンを押した際に、自分の子に有る子メニューを表示させる。
    (既に開いている場合は閉じさせる)
  4. 状況に応じてアイコンを差し替える(開いてる時は「-」に変更させる)
  5. 現在開いているページがメニューの中にあるかをチェックし、それが下の方の階層にいる場合はその階層まで開く

うん。けっこうやること有るな…。

では順番にやっていきます。

リスト用のアイコンを用意する

適当に画像を用意します。素材集を使ってもいいですし、自力で頑張って作成しても構いません。

自分はこんなかんじに作りました。名前はなんでもいいと思いますがわかり易い名前で保存します。(名前を変えた場合は以下それにあわせて読み替えて下さい)

sidenavTree_off sidenavTree_off.gif 子を持っているツリーが閉じている状態用
sidenavTree_on sidenavTree_on.gif 子を持っているツリーが開いている状態用
sidenavTree_noChild sidenavTree_noChild.gif 子を持っていないアイテム用

カスタムウィジェットを作る

今回は専用にカスタムメニューウィジェットを作ってメニューを書き出すときにアイコンを追加させますので、まずはウィジェットをつくります。

新しい PHP ファイルを作成し、こんなふうに書きます。

まぁ別に functions.phpに書いてもいいんですが…後々プラグインとして切り分けるかもしれないので別ファイルにしておくといいかもしれません。

プラグインにしない場合でも結構コードが長いんでただでさえ functions.php はカオスになりやすいわけですし、なるべく分けたほうがいいのかなとおもいます。個人的には。

このファイルを functions.php に結合させればとりあえず今作成したカスタムメニューを呼び出すためのウィジェットと管理画面が作成できます。

Walker Classを用意して表示内容を更にカスタマイズする

続いて、ウィジェットのなかで呼び出すメニューで使用する walker class を作成します。今作ったファイルに追記します。

 jQuery で開閉動作などを作成する

最後に jQuery を書きます。

 なんか後半の方かなり強引になってしまってるんでもうちょいスマートな書き方ができればいいんですが…うーむ。

画像の差し替えは replace をつかって画像タグの src の値を書き換えています。これ自体は on off 2枚の画像を使用してハイライトするボタンを作る時なんかには結構見られる手法ですね。

あとは…たいしたことはやってないです。
クラスをチェックして、自動で開くのか、手動で開くのか…みたいなかんじです。

子があるかを判定する方法

メニューの情報も実は投稿です。ある種のカスタム投稿みたいなものなのかな?

nav_menu_item という投稿タイプで登録されています。

なので、そのメニューのカスタムフィールドを使って絞り込みで get_posts することで調べることができます。

_menu_item_menu_item_parent は名前から 現在のメニューの親に当たるメニューのIDだと予想できますので、ここに自分のメニューIDがいれば、自分を親とするメニューが居る=子メニューがあると判定できそうです。

子が1個でもあればOKなので、先ほどの条件にして1個だけ投稿を取得します。オブジェクトがかえってくれば子あり、そうでなければ($children が空ならば)子はいないというふうにできますね。

今回は子メニューがあるときと無いときで先頭のアイコンを変更しますので、

こういうふうにして出し分けました。

メニューの中に現在のページが含まれているかを調べる

walker class を作るときに受け取る $item のなかにはメニューのいろんな情報が格納されているようで、 custom walker をいじる時もここを参照したりしてますよね。なので、ここになんかヒントがないかと思い、おもむろに print_r($item); してみましたところ、ほんとに色々出てきました。

IDだったり、メニューの中に放り込まれる各種クラスに関する情報だったり…。

で。見つけました。
[current]と、[current_item_ancestor]、そして[current_item_praent]というものがいまして、メニューごとに 「1」または「(空)」というふうになっていました。

もうだいたいわかると思いますが、

current 現在の投稿へのリンクをもつメニューのとき「1(true)」
current_item_ancestor 現在の投稿まで繋がる「先祖」のとき「1」
(ancestorは先祖の意)
current_item_parent 現在の投稿の「親」のとき「1」

つまり、$item->current_item_ancestor を調べてあげることで、親の親までまとめてクラスを与えられます。

あとは jQuery を駆使してごにょごにょすることで普段は閉じているけど、ツリーの中に今表示中のページが有る時はそこまでのツリーは開く

という作りたかった動きがつくれるようになりました。

TAGS