Having identical titles leads to identical ids and anchors

AI-generated summary

The discussion addresses an issue where identical heading titles in a document lead to identical IDs and anchors, causing problems with Table of Contents (TOC) navigation and scrolling highlighting. anderer reports the issue, noting that headings like “Aim” and “Slides” appear multiple times, resulting in non-unique IDs. Canapin clarifies that while it’s not a bug in HTML itself, the DiscoTOC theme component generates duplicate IDs based on heading content. RGJ confirms that unique IDs are not being generated, pointing to a bug in the theme component’s ID generation logic, specifically in the slugify function, and suggests a fix by incorporating the heading index into the ID to ensure uniqueness. Canapin provides visual evidence of the issue, and RGJ proposes a code change to fix it, with the topic tagged as pr-welcome for contributions.

Hi,

I discovered a bug (?) or technical limitation. the links of the TOC as well as the highlighting when scrolling down don’t work properly if the headings have the same name. i guess it is because not a unique ID is generated but the ID is only the heading’s name.

in my case the TOC looks like this - coming from an educational background the structure is always the same for our learning units, e.g.

  • H1 - Overview
  • H1 - 0:00 > 0:15 - Plenary and Introduction
    • H2 - Aim
    • H2 - Content
    • H2 - Method
    • H2 - Hints
    • H2 - Slides
  • H1 - 0:15 > 0:45 - Groupwork
    • H2 - Aim
    • H2 - Content
    • H2 - Method
    • H2 - Hints
    • H2 - Slides
  • H1 - 0:45 > 0:60 - Individual Work
    • H2 - Aim
    • H2 - Content
    • H2 - Method
    • H2 - Hints
    • H2 - Slides
  • H1 - 0:60 > 0:90 - Plenary Discussion
    • H2 - Aim
    • H2 - Content
    • H2 - Method
    • H2 - Hints
    • H2 - Slides

I guess we could make the H2-Titles more rich by giving more information, but this runs counter to having a clear structure across all learning units - and especially in the case of “hints” and “slides” there is not so much more that we could write.

Maybe this whole issue is related to the above mentioned issue … but i am not an expert enough.

6 Likes

It’s not a bug as it is inherent to HTML itself, so it’s a technical limitation.

A video of this behavior using the theme component as you explain:

I think it’s quite low priority, but a change could be welcome, so I’ll add a pr-welcome tag on it if someone wants to give it a shot. :slight_smile:

1 Like

Hmm no, the anchors are getting unique name attributes

# Heading level A
foo

## Heading level B
bar

## Heading level B
bar

# Heading level A
foo

## Heading level B
bar

## Heading level B
bar

generates: (note the -1 -2 -3 etc)

<h1 dir="ltr">
  <a name="heading-level-a-1" class="anchor" href="#heading-level-a-1"></a>Heading level A
</h1>
<p dir="ltr">foo</p>
<h2 dir="ltr">
  <a name="heading-level-b-2" class="anchor" href="#heading-level-b-2"></a>Heading level B
</h2>
<p dir="ltr">bar</p>
<h2 dir="ltr">
  <a name="heading-level-b-3" class="anchor" href="#heading-level-b-3"></a>Heading level B
</h2>
<p dir="ltr">bar</p>
<h1 dir="ltr">
  <a name="heading-level-a-4" class="anchor" href="#heading-level-a-4"></a>Heading level A
</h1>
<p dir="ltr">foo</p>
<h2 dir="ltr">
  <a name="heading-level-b-5" class="anchor" href="#heading-level-b-5"></a>Heading level B
</h2>
<p dir="ltr">bar</p>
<h2 dir="ltr">
  <a name="heading-level-b-6" class="anchor" href="#heading-level-b-6"></a>Heading level B
</h2>
<p dir="ltr">bar</p>

The attributes’ values are based on the titles’ contents, so identical titles will have identical id and data-d-toc attributes:

Last “Aim” title:

Leads to the first Aim which has an id and data-d-toc attributes whose value is the same as the last ToC element:

1 Like

Exactly, so this is not an inherent limitation of HTML (actually, having identical id’s in a single document is a violation of the spec) but a bug in the theme component.

I guess this

const suffix = slugify(h.textContent) || index;
const id = h.getAttribute("id") || slugify(`toc-${h.nodeName}-${suffix}`);

needs to be something like

const suffix = slugify(h.textContent) || '';
const id = h.getAttribute("id") || slugify(`toc-${h.nodeName}-${suffix}-${index}`);
6 Likes