Constraining application of markdown render hooks

I’m using a markdown render hook (a modified render-link.html) to control the styling and behavior of internal and external links.

I want to know if I can limit this render hook’s range of application just to my posts’ main content sections. Currently, if any internal links appear elsewhere on the page — e.g. in the footer, or any other partial — they are also subject to the render hook.

Essentially, on a single page I’d like to style internal links which appear in the main body/{{.Content}} to be rendered differently from internal links appearing elsewhere on the page. Is it possible to govern link behavior inside (certain) partials?

Can you provide an example?

The answer depends on the partial code and how the partial was called.

I use the following partial (original source) to generate a backlinks section for any post that is referenced elsewhere in the site:

{{ $re := $.File.LogicalName }}
{{ $backlinks := slice }}
{{ range .Site.AllPages }}
   {{ if (findRE $re .RawContent) }}
      {{ $backlinks = $backlinks | append . }}
   {{ end }}
{{ end }}

{{ if gt (len $backlinks) 0 }}
  <div class="bl-section">
    <h4>Links to this note</h4>
    <div class="backlinks">
       {{ range $backlinks }}
          <a href="{{.RelPermalink}}"><p class="backlink-title">{{.Title}}</p>
          <p>{{.Summary}}</p></a>
{{ end }}
</div></div>
{{ end }}

This gives me the page title and auto-generated summary for any incoming links, and works fine on its own.

But I recently had the foolish idea to create page-previews-on-hover for any internal links — similar to wikipedia’s, though mine are much worse and CSS-only. To achieve this, I edited the markdown render-link hook to generate the following elements after any internal link:

{{ if not (strings.HasPrefix .Destination "http") }}
{{ $internal := urls.Parse .Destination}}
{{- if $internal.Path -}}
{{ $fragment := "" }}
{{- with $internal.Fragment }}{{ $fragment = printf "#%s" . }}{{ end -}}
{{- with .Page.GetPage $internal.Path }}{{ $internal = printf "%s%s" .RelPermalink $fragment }}
{{ end }}
{{ end -}}
<a class="internal-link" href="{{ $internal | safeURL }}"{{with .Title}}{{.}}{{end}}>{{ .Text | safeHTML }}</a>
<span class="internal-link-preview">
     <span class="internal-link-title">{{ with .Page.GetPage .Destination}}{{.Title}}</span>
     <span class="internal-link-summary">{{.Summary }}{{end}}</span>
</span>
{{- end -}}

And I then use CSS to display and reposition these elements only on hover. It’s inelegant but it sort of works.

But I now realize that my render-link.html is breaking (at least) two things:

  1. Messy summaries
    Because Hugo’s autogenerated summaries strip HTML tags, I’d hoped that any links which appear early in a post text would be stripped or ignored, as is the case for external links. The source <p>This is the first sentence of a post. Here is an <a href="gohugo.io">external link.</a> correctly renders as plain text in a summary.

    But because the render hook applies early on, any internal links have already been transformed with my additional <spans> before Hugo’s summary is created, and the whole mess is pushed through into the summary.

  2. Infinite loops
    If Post A includes a link to Post B, and Post B includes a link to Post A, Hugo falls into an infinite loop and crashes. This isn’t caused by the backlinks.html partial, but rather by the render hook, and I’m not sure why. It occurs whether the offending links would appear in the post .summary or not.

Given that the render hook appears to be the problem, and must necessarily apply prior to other Hugo processes, I’m probably going to need to give up on playing with internal links in this manner. But I wanted to ask on the off chance that this was due to a small, fixable error rather than me trying to abuse a render hook into doing something that it should never do.

As written, this is not true.

Render hooks are invoked when:

  1. Rendering your markdown content (.RawContent) to HTML (.Content)
  2. Passing markdown to the .Page.RenderString method

With a partial, the render hooks would only be invoked if:

  1. The partial containing markdown was called by a shortcode, and the shortcode was called with the {{% foo %}} notation
  2. The partial passed markdown to .PageRenderString

You’re absolutely correct. I see now there’s nothing wrong with the partial, the problem is how I’ve set up the render hook to add content to my internal links, which is getting pulled into .summary.

I’ll need to find another way — perhaps a new partial — to target internal links outside of the markdown rendering process.

1 Like