尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Hugo Page 方法 HasMenuCurrent:精准标记导航菜单祖先级高亮状态

Hugo Page 方法 HasMenuCurrent:精准标记导航菜单祖先级高亮状态 Hugo Page 方法 HasMenuCurrent精准标记导航菜单祖先级高亮状态【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo导读HasMenuCurrent是 Hugo 模板系统中用于判断当前页面是否位于某个菜单项的子级或后代层级的核心方法常与IsMenuCurrent搭配使用以在导航菜单中正确输出ancestor等祖先级高亮样式。本文以 docs/content/en/methods/page/HasMenuCurrent.md 为主线结合源码实现与测试用例讲解该方法的语义、调用签名、使用前提以及递归高亮的完整实战模板帮助你构建可访问性良好的多级导航菜单。方法概述签名与返回类型HasMenuCurrent是定义在Page对象上的方法用于报告给定的 Page 对象是否与给定菜单中、给定菜单项下某个子菜单项所关联的 Page 对象匹配。其方法签名为PAGE.HasMenuCurrent MENU MENUENTRY参数类型说明PAGEPage调用方法的当前页面对象模板中通常使用$currentPage : .获取MENUstring菜单标识符menu ID如main、footer对应site.Menus.mainMENUENTRYMenuEntry被检查的菜单项对象来自对site.Menus.main的range迭代返回值类型为bool当给定菜单项存在子菜单项child menu entry与当前页面关联时返回true否则返回false。关键语义章节section页面的后代匹配原文档特别强调了一个重要的语义细节如果与菜单项关联的Page对象是一个章节section页面那么该方法对该章节的任何后代页面descendant也都返回true。这意味着在 Hugo 的页面树中只要某个菜单项指向一个 section如/blog那么/blog下的所有文章页面都会让HasMenuCurrent返回true。这一设计使得在渲染博客类站点时父级Blog菜单项可以自动获得祖先高亮无需为每篇文章单独配置。与 IsMenuCurrent 的分工协作HasMenuCurrent与IsMenuCurrent是一对互补方法IsMenuCurrent判断当前页面是否恰好等于给定菜单项所关联的页面用于标记当前激活active状态HasMenuCurrent判断当前页面是否位于给定菜单项的子菜单树中或作为章节的后代用于标记祖先ancestor状态。两个方法的详细对比可参见 IsMenuCurrent 文档其调用签名与参数形式完全一致。完整实战模板菜单祖先级高亮以下是 HasMenuCurrent 原文档 提供的核心模板示例它遍历site.Menus.main中的每个菜单项并根据当前页面与菜单项的关系分别渲染三种状态{{ $currentPage : . }} {{ range site.Menus.main }} {{ if $currentPage.IsMenuCurrent .Menu . }} a classactive aria-currentpage href{{ .URL }}{{ .Name }}/a {{ else if $currentPage.HasMenuCurrent .Menu . }} a classancestor aria-currenttrue href{{ .URL }}{{ .Name }}/a {{ else }} a href{{ .URL }}{{ .Name }}/a {{ end }} {{ end }}模板逻辑说明先用{{ $currentPage : . }}将当前页面上下文保存为变量避免在range循环内上下文被覆盖IsMenuCurrent命中时输出classactive与aria-currentpage标记当前页HasMenuCurrent命中时输出classancestor与aria-currenttrue标记祖先层级其余情况输出普通链接。上述两个aria-current属性对屏幕阅读器等辅助技术至关重要page表示当前页面本身true表示该链接指向当前页面的祖先级位置。支持嵌套菜单的递归版模板原文档进一步指向了 菜单模板文档其中提供了一份可处理嵌套多级菜单的递归 partial 模板核心高亮判断逻辑与上述示例一致{{- $page : .page }} {{- range .menuEntries }} {{- $attrs : dict href .URL }} {{- if $page.IsMenuCurrent .Menu . }} {{- $attrs merge $attrs (dict class active aria-current page) }} {{- else if $page.HasMenuCurrent .Menu .}} {{- $attrs merge $attrs (dict class ancestor aria-current true) }} {{- end }} ... {{- with .Children }} ul {{- partial inline/menu/walk.html (dict page $page menuEntries .) }} /ul {{- end }} {{- end }}在该模板中HasMenuCurrent与IsMenuCurrent的返回值通过merge动态合并进a标签属性字典随后在range .Children中递归调用自身从而实现任意深度的菜单结构都能正确标注激活与祖先状态。使用前提front matter 定义或 pageRef 属性原文档以 [!NOTE]形式给出了使用该方法的一个强制前提使用此方法时你必须在 front matter 中定义菜单项或者在项目配置中定义菜单项时指定pageRef属性。原因在于HasMenuCurrent以及IsMenuCurrent需要将菜单项与具体的Page对象进行比对。若菜单项既没有在页面 front matter 中声明也没有通过pageRef关联到具体页面Hugo 无法为菜单项解析出对应的Page对象方法将永远返回false。方式一在 front matter 中定义在页面 front matter 中声明其归属菜单页面本身即为菜单项关联的 Page 对象title My Post menu: main: weight: 30方式二在项目配置中使用 pageRef在hugo.toml等项目配置中通过pageRef指向页面的逻辑路径[[menus.main]] name Products pageRef /products weight 10 [[menus.main]] name Hardware pageRef /products/hardware parent Products weight 1pageRef支持指向各种页面类型其取值规则可参考 菜单配置文档页面类型pageRef 示例home/page/books/book-1section/bookstaxonomy/tagsterm/tags/foo当配置中的pageRef无法匹配到任何页面时HasMenuCurrent与IsMenuCurrent均返回false详见 MENUENTRY.PageRef 方法文档这也是配置菜单时必须确保路径正确的原因。源码级原理HasMenuCurrent 的判定逻辑要真正理解该方法的行为边界可以阅读其底层实现 navigation/pagemenus.go。该方法定义在pageMenus类型上逻辑可分为三个阶段第一阶段章节祖先判定func (pm *pageMenus) HasMenuCurrent(menuID string, me *MenuEntry) bool { if !types.IsNil(me.Page) me.Page.IsSection() { if ok : me.Page.IsAncestor(pm.p); ok { return true } }当菜单项关联的页面存在且为 section 时直接调用页面树的IsAncestor方法判断当前页面是否为该 section 的后代。这正是原文档中章节的后代页面也返回 true这一语义的代码出处。第二阶段菜单归属校验if !me.HasChildren() { return false } menus : pm.pagem.Menus() if m, ok : menus[menuID]; ok { for _, child : range me.Children { if child.isEqual(m) { return true } if pm.HasMenuCurrent(menuID, child) { return true } } }若菜单项没有子项则直接返回false否则取当前页面声明的菜单集合Menus()递归检查子菜单项是否与当前页面相等或匹配。第三阶段无菜单声明时的兜底比对if pm.p nil { return false } for _, child : range me.Children { if child.isSamePage(pm.p) { return true } if pm.HasMenuCurrent(menuID, child) { return true } } return false }即使页面未在 front matter 中声明菜单只要子菜单项通过pageRef解析出的页面与当前页面一致同样返回true。整个方法通过递归遍历子菜单项实现任意层级的匹配这就是它能支撑深层嵌套菜单的原因。测试验证行为被测试用例明确锁定仓库中的集成测试对HasMenuCurrent的行为进行了断言可作为权威的行为参考。在 hugolib/menu_test.go 的TestMenusSectionPagesMenu测试中配置了sectionPagesMenu sect自动生成章节菜单并断言渲染sect1/p1页面时菜单项/sect1/Section One输出HasMenuCurrent标记因为p1是sect1章节的子页面渲染sect2/p3页面时菜单项/sect2/Sect2s输出HasMenuCurrent而其他章节菜单项输出-即 false只有页面自身声明的菜单项sect1/p1输出IsMenuCurrent。另一处TestMenuHasMenuCurrentSection对应 issue 9846见 hugolib/menu_test.go则验证了嵌套菜单场景菜单项Tests指向 section/tests其子项Test 1指向/tests/test-1。在渲染/tests列表页时Tests菜单项得到IsMenuCurrent true、HasMenuCurrent false——即使Tests含有子菜单项只要当前页面恰好是该菜单项自身就只命中IsMenuCurrent而非HasMenuCurrent。这些测试共同确认了一个关键区分IsMenuCurrent命中当前页自身HasMenuCurrent命中当前页所在的祖先层级两者互斥且优先级明确——模板中应先判断IsMenuCurrent再判断HasMenuCurrent。相关资源完整菜单渲染指南docs/content/en/templates/menu.md菜单定义方式自动 / front matter / 项目配置docs/content/en/content-management/menus.md项目配置中的菜单项属性与嵌套示例docs/content/en/configuration/menus.md姊妹方法IsMenuCurrentdocs/content/en/methods/page/IsMenuCurrent.mdpageRef的解析行为与失败兜底docs/content/en/methods/menu-entry/PageRef.md方法底层实现navigation/pagemenus.go行为验证测试hugolib/menu_test.go【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表