10

I'm building a website with Jekyll and Github-Page written in markdown. I want to make it easy to permalink to headers, the way most online documentation does.

I want to get a URL with a hash when I click on a header. Is there an easy way to achieve this in Kramdown or in Jekyll's config?

Markdown page

#### A regular header

A regular sentence.

Ideal result

<h4 id="a-regular-header"><a href="#a-regular-header">A regular header</a></h4>
<p>A regular sentence.</p>
Soviut
  • 88,194
  • 49
  • 192
  • 260
Etrain
  • 253
  • 3
  • 11

4 Answers4

8

You can do this manually in the Markdown for each title:

#### [A regular header](#a-regular-header)

A regular sentence.

The downside there is the maintenance cost. Another option would be to create the links on the client side by adding this to the bottom of your chosen pages:

<script>
    var headings = document.querySelectorAll("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]");

    for (var i = 0; i < headings.length; i++) {
        headings[i].innerHTML =
            '<a href="#' + headings[i].id + '">' +
                headings[i].innerText +
            '</a>';
    }
</script>

You could look into a plugin, modified Markdown parser or task runner like gulp.js to do this at build time if the JavaScript dependency doesn't suit your audience. You can't use these alternatives if you want GitHub Pages to build your page, but you can build locally and commit the output.

Ross
  • 2,701
  • 16
  • 25
  • Thanks, I ended up using a JS solution like this, but I think I'll borrow your more intelligent query selector. – Etrain Nov 08 '16 at 03:19
7

You can use Jekyll plugins to override the standard behaviour. I put this in _plugins/header.rb:

class Jekyll::MarkdownHeader < Jekyll::Converters::Markdown
    def convert(content)
        super.gsub(/<h(\d) id="(.*?)">/, '<h\1 id="\2"><a href="#\2">§</a>')
    end
end

The regexp replaces all headers with an ID tag with one that also adds a link. This is not a fool-proof way to do this in a generic case (e.g. <h2 class="foo" id="x") won't work), but the Kramdown output is fairly reliable and consistent, so it should be okay. I added this with Jekyll 3.8.4 and Kramdown 1.17.0.

If you want/need a more robust solution then you can use a HTML parser. Shouldn't be that much harder.

The advantage of this is that it doesn't require JavaScript on the client side.


Or if you want to link the actual heading instead of prepending a link:

class Jekyll::MarkdownHeader < Jekyll::Converters::Markdown
  def convert(content)
    super.gsub(/<h(\d) id="(.*?)">(.*)<\/h(\d)>/, '<h\1 id="\2"><a href="#\2">\3</a></h\1>')
  end
end
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
6

If you want a GitHub Pages compatible way of doing this without JavaScript, you can get crazy creative with Liquid and carefully parse your rendered HTML as strings and manipulate it as such.

Or you could just use this snippet that does exactly that: https://github.com/allejo/jekyll-anchor-headings

allejo
  • 2,076
  • 4
  • 25
  • 40
1

This isn't possible from a generator standpoint without a Jekyll plugin. Since you're using GitHub Pages, that won't be possible since they don't support custom plugins.

A reasonable solution would be to create a script which finds all the headings with IDs and turns them into links or appends a link icon.

Soviut
  • 88,194
  • 49
  • 192
  • 260