Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highlighting CurrentMenuItem in a Hugo menu that is defined in config file

Tags:

hugo

I have had problems with getting the 'current menu item' to highlight in Hugo for as long as I've been using it (at least two years).

It appears possible if I define the menu using the frontmatter in my content files (i.e., individually adding each piece of a content to a menu). However, I don't want to litter the frontmatter of my content files with this information, especially seeing as how it is possible to define your menus using the main config file.

Here is how I declare my menu in my config file (I prefer to use JSON instead of YAML):

"menu": {
    "main": [
        {
            "name": "Home",
            "weight": 1,
            "url": "/"
        },
        {
            "name": "About",
            "weight": 2,
            "url": "/about"
        },
        {
            "name": "Blog",
            "weight": 3,
            "url": "/blog"
        },
        {
            "name": "Contact",
            "weight": 4,
            "url": "/contact"
        }
    ]
},

And here is my menu template

<ul class="nav">
    {{ $currentPage := . }}
    {{ range .Site.Menus.main }}
    {{ if .HasChildren }}
    <li class="nav-item nav-item--has-submenu{{ if $currentPage.HasMenuCurrent " main" . }} nav-item--active{{ end }}">
        <a class="nav-link" href="javascript:;" title="{{ .Name }}">
            {{ .Pre }}
            <span>{{ .Name }}</span>
        </a>
        <ul class="nav-submenu">
            {{ range .Children }}
            <li class="nav-item{{ if $currentPage.IsMenuCurrent " main" . }} nav-item--active{{ end }}">
                <a class="nav-link" href="{{ .URL }}" title="{{ .Name }}">
                    <span>{{ .Name }}</span>
                </a>
            </li>
            {{ end }}
        </ul>
    </li>
    {{else}}
    <li class="nav-item">
        <a class="nav-link" href="{{ .URL }}" title="{{ .Name }}">
            {{ .Pre }}
            <span>{{ .Name }}</span>
        </a>
    </li>
    {{end}}
    {{end}}
</ul>

I know that the markup above is specifically checking the frontmatter of each piece of content, and I understand why it isn't working. All I would like to know is what changes I must make in order to highlight the current menu item.

I think defining menus via the config file is for adding non-Hugo links to a menu and thus, there is no need to highlight those items. Is this even possible, or am I going to have to accept that I need to use the frontmatter within my content in order to build my menus?

Thanks

like image 921
Donny Burnside Avatar asked Mar 08 '23 00:03

Donny Burnside


1 Answers

I see two issues, either of which could cause problems.

The first issue, and the one I think is causing your trouble, is that you aren't ending your URLs with a slash. Hugo normalizes all URLs to end in a slash, and will redirect to the correct URL, but when .IsMenuCurrent and .HasMenuCurrent compare URLs to see if they refer to the same resource, they don't take this into account; they only see that /blog and /blog/ are different.

Try using:

"menu": {
  "main": [
    {
      "name": "Home",
      "weight": 1,
      "url": "/"
    },
    {
      "name": "About",
      "weight": 2,
      "url": "/about/"
    },
    {
      "name": "Blog",
      "weight": 3,
      "url": "/blog/"
    },
    {
      "name": "Contact",
      "weight": 4,
      "url": "/contact/"
    }
  ]
},

The second issue is that the .IsMenuCurrent and .HasMenuCurrent methods don't take into account that you may be running a multilingual site, in which a single standard URL like /blog/ from your menu config may be rewritten /en/blog/ or /es/blog/ via relLangURL in order to correctly link to the appropriate page for the current language. So, if you are trying to keep your menu config DRY, you will probably find that those two methods only work for (at most) your primary language.

This doesn't seem to apply to you, based on the template you provided, but I'll include the solution for others who find this question.

In order to fix this you either need to maintain separate menus for each language, which is a pain, or use your own logic to determine if the menu entry points to the current page. Fortunately, it's pretty straightforward:

{{ $currentPage := . }}
{{ range .Site.Menus.main }}

    {{ $menu_item_url := .URL | relLangURL }}
    {{ $page_url:= $currentPage.RelPermalink | relLangURL }}

    {{ if eq $menu_item_url $page_url }}
        {{/* the menu item links to the current page (with relLangURL) */}}
    {{ end }}

{{ end }}
like image 95
Dan Welty Avatar answered May 01 '23 13:05

Dan Welty