Is my approach correct for displaying an anchor link next to the headline?

Hello,
I already had this render-heading code that the user @jmooring kindly provided to me, which allows me to increase the size of each heading by one.

{{- $level := math.Min (add .Level 1) 6 -}}
<h{{ $level }} id="{{ .Anchor }}">
  {{- .Text -}}
</h{{ $level }}>

I want to display an anchor next to the headline, a # sign that appears when I hover over the headline, so I extended the code like so:

{{- $level := math.Min (add .Level 1) 6 -}}
<h{{ $level }} id="{{ .Anchor }}">
  {{- .Text -}}
  <a href="#{{ .Anchor }}" class="anchor"></a>
</h{{ $level }}>

Then added this CSS:

.anchor::before {
  content: "#";
  visibility: hidden;
  margin-left: .3em;
}

h3:hover > .anchor::before,
h4:hover > .anchor::before,
h5:hover > .anchor::before,
h6:hover > .anchor::before {
  visibility: visible;
}

It works, but I’m not sure if I did it correctly or if there’s a better approach. I would appreciate any suggestions.

That’s a CSS question (and perhaps concerning accessibility, too). From a first glance it looks syntactically ok, if a bit too complicated for modern CSS. And hover doesn’t work well on mobile devices.

Thank you. I was interested in the _markup/render-heading.html part. For me it looks a bit weird to have an anchor element with no content inside it. I mean when it comes to the HTML markup itself.

Then that’s a HTML question. An empty a element indeed is not ideal (perhaps not even allowed). Just embed the hx in the a. Or the Text. The net should be full of examples.

Thank you. I know that some empty elements can be addressed with an ARIA attribute. Anyway, I was curious if render-heading.html is the file that should handle this, meaning I was interested if Hugo is supposed to manage it there and if it’s okay to modify that file.

That’s a CSS question (and perhaps concerning accessibility, too).

I think CSS is complicated on its own by design. The hover trick I use is something I often see in hamburger menus, nothing out of the ordinary.

I know I’ve asked quite a few questions here on the forum. I’m a bit rushed to finish my page right now and I’m also feeling a bit stressed with a family member being sick. However, I definitely want to take the time to read the manual and educate myself because I know that asking questions doesn’t make sense without understanding things myself. I took a course by Todd McLeod on Go, but that was two years ago, and without practice, I’ve forgotten a lot. Also, Hugo has its own way of doing things. I’ll need to start reading the Hugo manual and approach it systematically.

  • you use a render hook ro render headings.
    So the best place is to add that there. Otherwise you may ger side effects if you distribute the code to multiple locations.

  • you already have a custom code just for you.

    So the answer is yes. You had that question before. If you need a custom version of a standard file. Create a copy in your local layouts and adjust.

1 Like

You may find a JS approach more flexible and capable, namely AnchorJS.

git clone --single-branch -b hugo-forum-topic-52165 https://github.com/jmooring/hugo-testing hugo-forum-topic-52165
cd hugo-forum-topic-52165
npm ci
hugo server

It works really well out of the box, handles heading elements that exist in content and in templates, and is not restricted to the Markdown content format (i.e., works with AsciiDoc, Emacs Org Mode, HTML, etc.).

And you can add an anchor to any element, not just headings.

2 Likes

Thank you. I’m using Hugo’s BuildJS.
I import a JavaScript file named main.js, which in turn imports other JavaScript files.

The import chunk from the baseof template:

{{- with resources.Get "js/main.js" }}
  {{- if eq hugo.Environment "development" }}
    {{- with . | js.Build }}
      <script src="{{ .RelPermalink }}" defer></script>
    {{- end }}
  {{- else }}
    {{- $opts := dict "minify" true }}
    {{- with . | js.Build $opts | fingerprint }}
      <script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous" defer></script>
    {{- end }}
  {{- end }}
{{- end }}

The main.js file:

import "./anchor";
import "./lightbox";
import "./custom";

I downloaded the anchor.js and it’s imported. I checked that in the browser’s view source. I try to call it from custom.js, where I put all my own JavaScript code, and it says:

Uncaught ReferenceError: AnchorJS is not defined
http://localhost:1313/js/main.js:210
http://localhost:1313/js/main.js:212

My custom.js file:

"use strict";

// Hamburger Menu
const menuToggleBtn = document.querySelector(".menu-toggle");
menuToggleBtn.addEventListener("click", event => {
  event.preventDefault();
  menuToggleBtn.parentElement.classList.toggle("open");
});

// Anchor JS
const anchors = new AnchorJS();
anchors.add('h3');

It’s like when I’m trying to access a variable that isn’t in the global scope. It’s strange. Importing with NPM would probably be easier, but I currently have everything imported as separate files and would prefer to keep it that way.

assets/
└── js/
    ├── anchor.js
    └── main.js
import AnchorJS from 'js/anchor';
document.addEventListener('DOMContentLoaded', function() {
  const anchors = new AnchorJS();
  anchors.add('h2:not(.no-anchor)');
  anchors.add('h3:not(.no-anchor)');
  anchors.add('h4:not(.no-anchor)');
  anchors.add('h5:not(.no-anchor)');
  anchors.add('h6:not(.no-anchor)');
});

https://gohugo.io/hugo-pipes/js/#import-js-code-from-assets

The import path can also be relative…

import AnchorJS from './anchor';
2 Likes

Thank you. It works!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.