HUGO

Surrounding HTML Heading Content with Anchor Tag?

Hello! I am trying to surround the h1, h2, h3, h4, h5, h6 HTML heading values within the {{ .Content }} page variable with an anchor tag linking to itself.

For example, if I had a markdown file containing:

# Introduction

This is my post.

The outputted HTML currently shows:

<h1 id="introduction">Introduction</h1>
<p>This is my post.</p>

What I would like to see is:

<h1 id="introduction">
  <a href="#introduction">Introduction</a>
</h1>
<p>This is my post.</p>

How would I go about achieving this? Thank you!

EDIT: I’ve looked into Goldmark Render Hooks, but creating layouts/_default/_markup/render-h1.html appears to have no effect on the output.

I think you could accomplish this with a custom shortcode.

You might have parameters called level and text, then the template would contain that full desired html example you provided, but substituting in the header number and content text where appropriate. You’d call “anchorize” on the header text to get the link and ID values.

Hi @kc0bfv, thank you for the suggestion. I’ve explored using custom shortcodes but these approaches break the Table of Contents.

I have a sample markdown file containing one h1 and two h2 headers with some content:

# Title

Lorem ipsum dolor sit amet.

{{< hanchor "## Anchor Title" >}}

Consectetur adipiscing elit.

## Non Anchor Title

Suspendisse potenti.

My custom shortcode layouts/shortcodes/hanchor.html:

{{ with .Get 0 }}
  {{ $data := split . " " }}
  <!-- Get the heading level, heading text, and anchorized id -->
  {{ $htype := chomp (delimit (first 1 $data) " ") }}
  {{ $hval := delimit (after 1 $data) " " }}
  {{ $hid := anchorize $hval }}

  <!-- Attempt 1: Construct new markdown, making the heading a link -->
  <!-- {{ print $htype " [" $hval "](#" $hid ")" | $.Page.RenderString }} -->
  <!-- {{ print $htype " [" $hval "](#" $hid ")" | markdownify }} -->
  <!-- This fails as anchorized id mismatches constructed a tag -->

  <!-- Attempt 2: Manual anchor id on heading and link -->
  {{ $hlen := len $htype }}
  {{ $stag := print "<h" $hlen " id=\"" $hid "\">" }}
  {{ $etag := print "</h" $hlen ">" }}
  {{ $link := print "[" $hval "](#" $hid ")" | markdownify }}
  {{ print $stag $link $etag | safeHTML}}
{{ end }}

The outputted {{ .Content }} looks okay:

<h1 id="title">Title</h1>
<p>Lorem ipsum dolor sit amet.</p>
<h2 id="anchor-title"><a href="#anchor-title">Anchor Title</a></h2>
<p>Consectetur adipiscing elit.</p>
<h2 id="non-anchor-title">Non Anchor Title</h2>
<p>Suspendisse potenti.</p>

Unfortunately my Table of Contents is no longer accurate as it is missing the anchor-title:

<nav id="TableOfContents">
  <ul>
    <li><a href="#title">Title</a>
      <ul>
        <li><a href="#non-anchor-title">Non Anchor Title</a></li>
      </ul>
    </li>
  </ul>
</nav>

Curious to hear if you have additional advice or next steps. Thanks!

Sorry, on mobile, so not able to actually try this out… Your TOC problem reminds me of the recommendation here:

Using {{% and %}} instead of {{< and >}} causes things to get rendered differently, potentially including them in TOC. I tested this out a bit ago and got confusing results with Goldmark… So I’m not sure exactly what’ll happen if you try this.

You may also have to drop the parameter into the template without calling markdownify on it? I’m really not sure.

Thanks for the quick reply! I just re-read the documentation for shortcodes.
If further rendering is required, use {{% and %}}.
If no further rendering is required (shortcode outputs html?), use {{< and >}}.

I updated layouts/shortcodes/hanchor.html to:

{{ with .Get 0 }}
  {{ $data := split . " " }}
  <!-- Get the heading level, heading text, and anchorized id -->
  {{ $htype := chomp (delimit (first 1 $data) " ") }}
  {{ $hval := delimit (after 1 $data) " " }}
  {{ $hid := anchorize $hval }}
  {{ print $htype " [" $hval "](#" $hid ")" }}
{{ end }}

My modified markdown file is now:

# Title

Lorem ipsum dolor sit amet.

{{% hanchor "## Anchor Title" %}}

Consectetur adipiscing elit.

## Non Anchor Title

Suspendisse potenti.

The outputted html and table of contents still is not quite right, as the link causes the anchor identifier to be duplicated.

<!-- TOC -->
<nav id="TableOfContents">
  <ul>
    <li><a href="#title">Title</a>
      <ul>
        <li><a href="#anchor-titleanchor-title">Anchor Title</a></li>
        <li><a href="#non-anchor-title">Non Anchor Title</a></li>
      </ul>
    </li>
  </ul>
</nav>

<!-- {{ .Content }} -->
<h1 id="title">Title</h1>
<p>Lorem ipsum dolor sit amet.</p>
<h2 id="anchor-titleanchor-title">
  <a href="#anchor-title">Anchor Title</a>
</h2>
<p>Consectetur adipiscing elit.</p>
<h2 id="non-anchor-title">Non Anchor Title</h2>
<p>Suspendisse potenti.</p>

A couple folks reference being able to specify header ID with syntax like:

# My Title {#my-id}

I’ve seen reference to this with both blackfriday and goldmark:

Since you calculate the hid, you might be able to add that extra bit to your print statement, specifying the correct ID.

This trick still works:

1 Like

Thank you @kaushalmodi , your post was very useful in solving my problem.

layouts/partials/anchorize-headings.html

{{ . | replaceRE "(<h[1-6]\\sid=\"([^\"]+)\"\\s?>)(.+)(</h[1-6]+>)" "${1}<a class=\"h-anchor\" href=\"#${2}\">${3}</a>${4}" | safeHTML }}

Used as {{ partial "anchorize-headings.html" .Content }}