Hugo初心者がテーマを自作した記録 ナビゲーションメニュー

2019-07-12 |
2019-11-02

The Hugo Gopher is designed by Renée French


ナビゲーションメニュー

前回はHugoのブログテーマに必要そうな、

  • 最新記事のリンクを表示
  • 記事を表示するページの作成と表示
  • カテゴリー(セクション)ページの作成と表示

の機能を作りました。

今回は、各ページに必要なナビゲーションメニューを作成していきたいと思います。

ナビゲーションメニューとは

私のブログを例に出すと、ヘッダー部分に表示されているメニューのことです。

この記事を書いている現在では、TOPとABOUTしかありませんが、サイトポリシーや、サイトマップ、記事一覧ページなど見てもらいたいページヘのリンクを表示させたりします。

このブログの重要なページへのリンクを表示しておく場所で、このブログのどこにいてもそれらのページにすぐアクセスできるようするメニューですね。

Hugoの機能で実装するモチベーション

正直なところ、ナビゲーションメニューはHugoの機能を使わずにHTMLとCSSで普通に実装してもいいと思います。

私も最初はHugoの機能の使い方がわからず、メニューを作成してその要素一つ一つにリンクを設定していました。

しかし、Hugoの機能を利用することで実際に記述するコード量が減り、メニューの管理が楽になります。

Hugoの機能でナビゲーションメニューを作る

ナビゲーションメニューを作成するのに、Hugoのconfig.tomlを利用します。

config.tomlに設定を書いておくことで、テンプレート側でその情報を参照することができます。

この機能を用いて、各ページでメニューを出力するときに、rangeを使ってconfig.tomlで設定したメニューの各要素を参照するという感じにします。

config.tomlの設定

Menus | Hugo

こちらを参考に進めていこうと思います。

content/posts/に、my-first-post.mdとmy-second-post.mdが存在しているという前提で話していきます。

config.tomlにおいて、次のように設定します。

以下の設定は、自分の設定したいメニューでの表示名と、そのファイルのurlに置き換えて設定してみてください。

[[ menu.main ]]
    name = "POST1"
    url = "/posts/my-first-post"
    weight = 1

[[ menu.main ]]
    name = "POST2"
    url = "/posts/my-second-post"
    weight = 2

このように書くことで、Hugoのテンプレート構文内において、

{{ .Site.Menus.main }}

とすると、menu.mainで設定した項目のコレクションを参照できます。

[[ menu.main ]]なのに、呼び出すときはMenus.mainになってたりと、ここらへんは地味にハマりポイントでした…

今回の場合ですと、nameがPOST1である[[ menu.main ]]と、nameがPOST2である[[ menu.main ]]を扱うことができます。

rangeを使ってナビゲーションメニューを作る

ここで、

{{ .Site.Menus.main }}

で返ってくる値がコレクションになっています。

このコレクションを用いてナビゲーションメニューを作成するには、メニューの各要素について処理を行う必要があります。

よって、rangeを使って各要素をメニューに組み込むための処理を行っていきます。

[[ menu.main ]]に設定した要素のリンクを作成するところまでやろうと思います。

{{ range .Site.Menus.main.ByWeight }}
<a href="{{ .URL }}">{{ .Name }}</a>
{{ end }}

このようにすることで、menu.mainに設定した各要素について、リンクタイトルがnameで設定したパラメータで、リンク先がurlで設定したパラメータになります。

また、.ByWeightをつけることで、weightで設定したパラメータについて昇順でリンクが作成されます。

メニューの表示順を指定するときは、config.tomlweightを指定して、rangeで各要素を操作するときに、その対象となるコレクションにByWeightをつけてソートすることを忘れないように気をつけます。

今回は、config.tomlにメニューの設定を書くことができて、rangeを用いてメニューの要素一つ一つを参照することができるというのが一番重要です。

あとは、今回やったようにリンクとして出力して横並びに出力するのか、リストとして出力してリストの要素をcssで横並びにするのかは、お好みに任せます!

私は、ナビゲーションメニューでは階層を表現しないようにしようと思いましたの、横並びのリンクとして出力してcssでスタイルをあてています。

階層をもったナビゲーションメニュー

オマケといってはなんですが、各メニューの要素について親子関係があるような場合があると思います。

たとえば、ナビゲーションメニューのなかにカテゴリーという項目があったとして、その子要素として各カテゴリーページへのリンクを表示したりすることはよくあることだと思います。

子要素の表現の仕方はドロップダウンメニューなど色々あると思いますので、そこは各自で実装していただくとして、config.tomlにおけるメニューの要素の親子関係の表し方について、

Menu Entry Properties | Hugo

公式ドキュメントに解説しているページがありますので、そこを参考にしながら解説していきます。

メニューの親子関係の設定方法

簡単な例を表示してから解説していきます。

親要素としてnameがPOSTSの要素、子要素としてnameがPOST1とPOST2の要素を考えます。

そのときに、メニューの親子関係の表し方は以下のようになります。

[[ menu.main ]]
    identifier = "POSTS"
    name = "POSTS"
    url = "/posts/"
    weight = 1

[[ menu.main ]]
    parent = "POSTS"
    name = "POST1"
    url = "/posts/my-first-post"
    weight = 1

[[ menu.main ]]
    parent = "POSTS"
    name = "POST2"
    url = "/posts/my-second-post"
    weight = 2

このように設定することで、親子関係を表します。

子要素は、parentに親のnameidentifierを設定します。

親要素は、メニューの要素のnameに重複がなければidentifierを設定しなくても問題はないですが、もし同じnameを設定した要素が存在する場合には、nameのかわりにidentifierを設定します。

メニュー要素内での重複に気をつけながらnameを設定してもいいと思いますが、identifierを設定して確実に識別できるようにしておいたほうがエラーに気づくやすくなると思います。

階層をもったナビゲーションメニューの出力

ナビゲーションメニューにおいて階層を表現する場合、ドロップダウンメニューなどになると思いますが、そこらへんの実装は各自で行ってもらうことにします。

今回は階層を持ったリストとしてメニュー要素を出力してみようと思いますので、この処理を参考に皆さんが必要としているカタチに変更してください。

以下のコードで出力できます。

{{ with .Site.Menus.main.ByWeight }}
  <ul>
    {{ range . }}
      <li>
        <a href="{{ .URL }}">{{ .Name }}</a>
        {{ with .Children.ByWeight }}
        <ul>
          {{ range . }}
            <li><a href="{{ .URL }}">{{ .Name }}</a></li>
          {{ end }}
        </ul>
        {{ end }}
      </li>
    {{ end }}
  </ul>
{{ end }}

まず、withを用いて.Site.Menus.mainが存在するかで分岐します。存在しない場合は何も出力しません。

存在する場合は、<ul>タグを出力し、それ以降の処理でリストの中身を生成していきます。

次に、with以下では、視点が.Site.Menus.mainになっているので、自分自身に対してrangeを適用していくことになります。

よって、 range . というふうに書くことで自分自身にrangeを適用することになります。

それぞれの要素について<li> タグでリストの要素として出力していきますが、ここで子要素があるかどうかでさらに分岐させる必要があります。

子要素は.Childrenでアクセスすることができるため、with .Childrenとすることで子要素が存在するときだけ、リストを入れ子にしていきます。

先ほどと同じように2回目のwith以下では、視点が.Childrenになっているため、自分自身に対してrangeを適用していくことになります。

よって、range . と書くことで、それぞれの要素について<li>タグを生成していきます。

今回は一つ下の階層まで表現するコードですが、更に下の階層まで表現したい場合は、

<li><a href="{{ .URL }}">{{ .Name }}</a></li>

となっている部分を、

<li><a href="{{ .URL }}">{{ .Name }}
{{ with .Children.ByWeight }}
  <ul>
  {{ range . }}
  <li><a href="{{ .URL }}">{{ .Name }}</a></li>
  {{ end }}
  </ul>
{{ end }}
</li>

というふうに追加していってください。

なんか再帰的に書けそうな気もしますね…

実際、Hugoの機能を使ってこの処理を再帰的に実行している例を見たことはあるのですが、自分は階層分だけ繰り返すように書けばいいかなって感じで深く入り込みませんでした。

まとめ

Hugoの機能を用いてナビゲーションメニュー作成について解説しました。

config.tomlにメニューのパラメータを設定しておけば、Hugoのテンプレート構文内でそこを参照できるようになり、rangeと組み合わせることでナビゲーションメニューを生成しました。

階層を持ったナビゲーションメニューについても簡単に解説しました。

with .Children によって処理を分岐させることで、子要素がある場合はリストを入れ子にして階層を表現しました。

参考文献

Menus | Hugo

Menu Entry Properties | Hugo

Please share, if you like this.