== User Story
As a reader, I want to be able to know which section I am on at all times, so that I have better context of what I am reading
== Design spec
=== States
| no highlighted section | top-level section highlighted | subsection highlighted
| {F34914613, height=400} | {F34914607, height=400} | {F34914610, height=400}
**Active section definition:**
- When a top-level section is highlighted, only that link should be highlighted
- When a subsection is highlighted, that link along with it's corresponding top-level link should be highlighted.
- Only one section (top-level or top-level + subsection) should be highlighted at any given time.
=== UI Behaviour
**On Scroll**
While scrolling the page, the ToC will:
- "Stick" to the top of the page when scrolling down the page.
- "activate" the link that corresponds to the current section.
**On click**
When clicking a link in the ToC:
- The page will jump to the corresponding heading
- "activate" the link that corresponds to the current section.
== The current "section" problem
The main challenge in highlighting the "current section" of an article is that our content HTML does not contain actual sections, instead it's a flat series of paragraphs and headings (as opposed to parsoid or the mobile formatter, which have sections).
When implementing the [[ https://en-toc.wmcloud.org/wiki/Main_Page | user-facing prototype ]], [[ https://css-tricks.com/table-of-contents-with-intersectionobserver/ | several ]] [[ https://codepen.io/j4n/details/XWaRgKZ | approaches ]] to this problem were considered, however, leaning heavily on intersection observer was problematic because as explained in [[ https://stackoverflow.com/questions/61951380/intersection-observer-fails-sometimes-when-i-scroll-fast | this stackOverflow question ]], intersection observer is not optimized for "high velocity" contexts (i.e fast scrolling), and therefore, is prone to missing section titles when they scroll by quickly.
The most reliable solution turns out to be looping through each heading and returning **the last heading that is at or above the top of the page** as the current section.
```lang=js
const headings = document.getElementById( 'bodyContent' ).querySelectorAll( 'h1,h2,h3,h4,h5,h6' );
[ ...headings ] // Convert to array.
.reverse() // Reverse because find gives us the first match, we want the last one.
.find( h => h.getBoundingClientRect().top <= 0 ); // If the heading is at or above the top of the page, then that's our current section.
```
Initially, this check was run during a scroll event, however, it's proven to be equally effective when running as an intersection observer callback with no `rootMargin` (which seems to increase the probability that the observer will catch the headings).
```lang=js
const headings = [ ...document.getElementById( 'bodyContent' ).querySelectorAll( 'h1,h2,h3,h4,h5,h6' ) ].reverse();
function findCurrent() {
const h = headings.find( h => h.getBoundingClientRect().top <= 0 );
// console.log( h ? h.innerText : 'no active section, at top of page' );
return h;
}
let observer = new IntersectionObserver( findCurrent );
headings.forEach( h => observer.observe( h ) );
```
=== Linking the active section to headings
Setting the active section in the ToC requires matching the current article section heading with a link somewhere in the ToC.
Section headings //should// have `id` attributes that match the `href` value of the ToC links, however, there could be complications caused by unicode-encoding and mediawiki specific formatting that could produce invalid `id` attributes or attributes that don't match the`href`.
(We've been here before T238385)
This time around, we //could// try `CSS.escape` to guard against these issues (it's [[ https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape | not spec compliant ]], but has decent browser support:
```lang=js
tocElement.querySelector( `a[href="#${CSS.escape( currentSectionHeading.id )}"]` );
```
**Proof of concept (browser extension)**
https://github.com/jandre3000/desktop-improvements-extension/blob/993193a2e56e5f5081a230fa5ffbfb2743b183de/app/content/prototypes/tableOfContents/toc.js#L275
== Acceptance criteria
[] The section that a user currently has open (defined as having scrolled past the heading section for that section but before the heading section for the following section) should appear in bold
[] If a page has subsections, the subsection name and the section name should appear as bold whens scrolled to