Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heading levels in Markdown table of contents #1778

Closed
Parent5446 opened this issue Jan 14, 2016 · 44 comments
Closed

Heading levels in Markdown table of contents #1778

Parent5446 opened this issue Jan 14, 2016 · 44 comments

Comments

@Parent5446
Copy link

So I'm not sure if this is at all possible, or what the best way would be to do this, but we're having some issues using the .TableOfContents variable in templates.

There are a couple of points:

  1. Semantically, HTML should only have one <h1> tag per section root.
  2. It would be nice to have the top-level, i.e., <body>-level, <h1> tag generated in the layout template using the .Title attribute of the page. (It works better semantically, rather than having the title in two places.)
  3. When rendering Markdown, the table of contents in .TableOfContents, sensibly, only renders navigation for the headers in the actual content.
  4. Furthermore, the .TableOfContents always treats <h1> as top-level, even if there are no <h1>-level headers.

Because of this, if you comply with (1) and implement (2), and thus only have <h2> or lower headers in your content, the generated table of contents contains an empty top-level <nav> as a result of (3) and (4).

Example table of contents:

<nav id="TableOfContents">
    <ul>
        <li>
            <ul>
                <li><a href="#introduction:e95c9b0e3cf17661856239295171d427">Introduction</a></li>
                <li><a href="#at-a-glance:e95c9b0e3cf17661856239295171d427">At a Glance</a></li>
            </ul>
        </li>
    </ul>
</nav>

This messes with the page semantically since now the navigation has an empty top-level. The way I see it there are two ways to fix this:

  1. Somehow get the header tags in layout templates into the table of contents, so the top-level is not blank.
  2. Get the renderer to remove empty levels, e.g., by treating <h2> as top-level headers if there is no <h1> in the content.

I'm not sure if there is currently an undocumented workaround to implement either of these solutions. But if there isn't, would there be a way to allow for using either of the two solutions to achieve a more semantic table of contents?

@bep
Copy link
Member

bep commented Jan 14, 2016

I have not read this in detail, but we get the ToC from https://github.com/russross/blackfriday -- so maybe it is better to discuss it there.

@Dr-Terrible
Copy link

This messes with the page semantically since now the navigation has an empty top-level. The way I see it there are two ways to fix this: [...]

I am affected by this odd behaviour too. The way .ToC is rendered by Hugo/Blackfriday makes the entire concept of ToC pretty much useless. In the worst case scenario, my ToC is completely messed up and doesn't reflect any more the original header structure in my markdown files. Some times, the ToC's headers are so messed up they aren't correctly rendered by the browser.

The issue here is that blackfriday spits out a bunch of hard coded HTML tags, and then Hugo wrap them in a way that is neither valid HTML code.

A solution is quite simple: don't give users a preformatted .ToC, just give them an indexed array and then let them generate the desired HTML structure by iterating over the array elements.

@moorereason
Copy link
Contributor

@Parent5446 and @Dr-Terrible,
It would be great if one of you would create a blackfriday issue for this.

@bep
Copy link
Member

bep commented Jan 28, 2016

The bottom line of this is:

The ToC should not be HTML, it should be a datastructure that people can do with as they please.

@ErjanGavalji
Copy link

Okay, for some reason, even though toc_levels explicitly specified to 1..6 both in page and in config file, kramdown does not generate ids.

I'm putting this on hold for the time being for I'd prefer to test it with a newer jekyll version (and dependencies), which would take some more time.

I found that the level4 subsections are not that large, so we could get without links there. What do you think, guys?

Cheers,
Erjan

@helmbold
Copy link

I've written a little tool that removes the unnecessary level of nesting from the table of contents. You can run it, after Hugo has generated the contents in the "public" folder.

@DavidCRivera
Copy link

I'm running into an issue that may be related. It seems that when I have a lower level tag i.e. an H6 appearing first in my content before an H5, a TOC is rendered at the start of my content, without my explicitly including a .TableOfContents tag.

What I mean is, if my content looks like:

##### This is an H5 #####

###### This is an H6 ######

Then all is right with the world, and no TOC gets generated. But if it looks like this:

###### This is an H6 ######

##### This is an H5 #####

Then I get the garbled TOC HTML appearing arbitrarily at the start of my content. It doesn't even have the "TableOfContents" ID; it's just a naked NAV tag. Seems like a bug...

@bep
Copy link
Member

bep commented Feb 28, 2017

This issue has been automatically marked as stale because it has not been commented on for at least four months.

The resources of the Hugo team are limited, and so we are asking for your help.

If this is a bug and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.

This issue will automatically be closed in four months if no further activity occurs. Thank you for all your contributions.

@bep bep added the Stale label Feb 28, 2017
@bep
Copy link
Member

bep commented Mar 1, 2017

Note/Update: This issue is marked as stale, and I may have said something earlier about "opening a thread on the discussion forum". Please don't.

If this is a bug and you can still reproduce this error on the latest release or the master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.

@bep bep added Keep and removed Stale labels Mar 10, 2017
@helmbold
Copy link

This issue is still unresolved.

@thewebmastercom
Copy link

Any news on this? It appears to be still unresolved.

@mikeblum
Copy link

mikeblum commented Jul 9, 2017

I found this open issue when trying to figure out why Hugo's {{ .TableOfContents }} didn't seem to work properly / wasn't styleable. I created a partial for generating TOC trees based on header tags. The general philosophy being that TOCs need to be customisable so I figured that'd work best with a partial. This is more of a way to render headers within a .Content block rather than a formal data structure.

snippet from partials/table-of-contents.html:

 <!-- ignore empty links with + -->
{{ $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content }}
<!-- at least one header to link to -->
{{ $has_headers := ge (len $headers) 1 }}
<!-- a post can explicitly disable Table of Contents with toc: false -->
{{ $show_toc := (eq $.Params.toc true) }}
{{ if and $has_headers $show_toc }}
<div class="table-of-contents toc bd-callout">
    <!-- TOC header -->
    <h4 class="text-muted">Table of Contents</h4>
    {{ range $headers }}
        {{ $header := . }}
        {{ range first 1 (findRE "<h[1-6]" $header 1) }}
            {{ range findRE "[1-6]" . 1 }}
                {{ $next_heading := (int .) }}
                <!-- generate li array of the proper depth -->
                {{ range seq $next_heading }}
                    <ul class="toc-h{{ . }}">
                {{end}}
                {{ $base := ($.Page.File.LogicalName) }}
                {{ $anchorId := ($header | plainify | htmlEscape | urlize) }}
                {{ $href := delimit (slice $base $anchorId) "#" | string }}
                <a href="{{ relref $.Page $href }}">
                    <li>{{ $header | plainify | htmlEscape }}</li>
                </a>
                <!-- close list -->
                {{ range seq $next_heading }}
                    </ul>
                {{end}}
            {{end}}
        {{end}}
    {{ end }}
</div>
{{ end }}

Here is how I have it working to render TOCs for Posts:

 <div class="content">
          {{ partial "banner" . }}
          {{ partial "table-of-contents" . }}
          <!-- supports emoji -->
          {{ .Content | emojify }}
</div>

@jirfag
Copy link

jirfag commented Oct 8, 2017

@mikeblum thank you for this snippet! I used it and made bootstrap-styled table of contents, my snippet is here

@vassudanagunta
Copy link
Contributor

Once we have #1778, we can more easily provide the TOC as a data structure, using access to the syntax tree, or perhaps writing a new renderer to build during parsing.

@vassudanagunta
Copy link
Contributor

Once we have #1778

I meant to say, once we have #3949 (Upgrade to Blackfriday v2)...

@lb13
Copy link

lb13 commented Feb 27, 2018

@mikeblum thank you!

@alexislg2
Copy link

@mikeblum thanks a lot!
I still have an issue with the anchor link. This ($header | plainify | htmlEscape | urlize) does not work with several cases. Examples:

Bonjour, ca va ? shoud return bonjour-ca-va but returns bonjour-ca-va- (note the hyphen at the end)

Also, it does not work with apostrophes. Both href and title do not work. for example let's go gives letamprsquos-go

I am not a go expert so I cannot help.

@branw
Copy link

branw commented May 27, 2018

@alexislg2 The last two functions are incorrect. plainify returns a string that is already escaped, so we have to htmlUnescape it. Furthermore, anchors are generated using the anchorize function (a BlackFriday provided feature), not urlize.

Here are the relevant changes to get the partial @mikeblum posted working correctly:

{{ $anchorId := ($header | plainify | htmlUnescape | anchorize) }}
{{ $href := delimit (slice $base $anchorId) "#" | string }}
<li><a href="{{ relref $.Page $href }}">
    {{ $header | plainify | htmlUnescape }}
</a></li>

@skyzyx
Copy link

skyzyx commented Sep 3, 2018

I implemented the toc as a partial using code from above, but the logic of the code produced markup that was invalid and not semantically sound. So I rewrote it like so:

https://gist.github.com/skyzyx/a796d66f6a124f057f3374eff0b3f99a

This version intentionally only looks for h2h4. This is because the page title is the h1, and everything else is h2 or below. I also choose to stop at h4 because the value to the reader beyond that is — in my experience — negligible.

Feel free to re-adjust the regexes if you want a broader spectrum of headers.

yihui added a commit to yihui/yihui.org that referenced this issue Sep 10, 2018
…i.e. h1) but lower levels (h2, h3, ...)

it seems Hugo/Blackfriday don't want to fix it: gohugoio/hugo#1778
@yihui
Copy link
Contributor

yihui commented Sep 10, 2018

In case any one is interested, I just wrote a short JS script to remove the non-existent h1 in TOC so that it can start from h2 instead: https://github.com/yihui/misc.js/blob/main/js/fix-toc.js One advantage of this solution is that it does not assume whether your TOC starts from h1 or h2.

You can include the script via something like <script src="/js/fix-toc.js></script> after you put it under the static/js/ directory of your site.

Here is an example. If your eyes are quick enough, you can actually see the first <ul> in TOC quickly removed :)

@VincentTam
Copy link

VincentTam commented Sep 17, 2018

@skyzyx Thanks for sharing. I'm implement this in my Hugo blog on GitLab under /pages/*/index.md, but this generates an unordered list of links pointing to /post/*/index.md. I believe I'll probably end up with errors similar to those in my recent failed job.

@yihui Thanks for your JavaScript, even though it works only if there's more than one section. I've adapted your script to Beautiful Hugo and published it on GitLab snippet.

// Copyright (c) 2017 Yihui Xie & 2018 Vincent Tam under MIT

(function() {
  var toc = document.getElementById('TableOfContents');
  if (!toc) return;
  do {
    var li, ul = toc.querySelector('ul');
    if (ul.childElementCount !== 1) break;
    li = ul.firstElementChild;
    if (li.tagName !== 'LI') break;
    // remove <ul><li></li></ul> where only <ul> only contains one <li>
    ul.outerHTML = li.innerHTML;
  } while (toc.childElementCount >= 1);
})();

bep added a commit to bep/hugo that referenced this issue Nov 11, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
bep added a commit to bep/hugo that referenced this issue Nov 12, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 14, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 15, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 16, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 16, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 17, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 19, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 19, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 19, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 19, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 23, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 23, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 23, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 23, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
bep added a commit to bep/hugo that referenced this issue Nov 23, 2019
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes gohugoio#5963
Fixes gohugoio#1778
Fixes gohugoio#6355
@bep bep closed this as completed in bfb9613 Nov 23, 2019
@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests