1

In my html page I have a table of contents with CSS attribute position: fixed; and would like to highlight (bold or italics) the current reading/displaying position there while scrolling down the page.

                             | yada yada yada ...
1. Section 1                 |
 1.1 Subsection 1            | 1.2 Subsection 2
 1.2 Subsection 2 <-- bold   |   
2. Section 2                 | Lorem ipsum dolor sit amet,
[...]                        | consectetur adipisici elit
                             | [...]

I would like to keep it simple and CSS -- if at all possible -- would be preferred over JS. The site is being generated by Hugo but the answer doesn't necessarily need to be Hugo specific.

Thanks a lot!


P.s. In case of a Hugo specific answer: The toc is being generated using {{ partial "table-of-contents" . }}

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 }}
Shaunak
  • 16,227
  • 4
  • 48
  • 81
Suuuehgi
  • 2,680
  • 2
  • 20
  • 26

1 Answers1

1

In a scroll you can track the visibility of you content headers, if your content header and index items have id or data attributes in a naming convention.

Then, the one that is visible you can find it in index using the naming convention and add a highlight class on it.

Here's a working example: https://jsfiddle.net/az6z8rze/24/

The real trick here is the isElementInViewport function. You can use it as it as, and easily adapt the simplified example here to nested index as well.

function isElementInViewport (el) {
    
    //special bonus for those using jQuery
    if (typeof $ === "function" && el instanceof $) {
        el = el[0];
    }
  
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
        vHeight  = window.innerHeight || doc.documentElement.clientHeight,
        efp      = function (x, y) { return document.elementFromPoint(x, y) };     

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < 0 
            || rect.left > vWidth || rect.top > vHeight)
        return false;

    // Return true if any of its four corners are visible
    return (
          el.contains(efp(rect.left,  rect.top))
      ||  el.contains(efp(rect.right, rect.top))
      ||  el.contains(efp(rect.right, rect.bottom))
      ||  el.contains(efp(rect.left,  rect.bottom))
    );
}


var handler = function(){
  var visible = 0;
  [1,2,3,4].forEach(function(id){
    if(isElementInViewport($('#'+id))) visible = id;
  })
  
  if(visible){
   $('#index div').removeClass('active');
    $('#index-'+visible).addClass('active');
  }
};



//jQuery
$(window).on('DOMContentLoaded load resize scroll', handler);
#index{
  width: 100%;
  position: fixed;
  top:0;
  left:0;
  background: white;
}

.active{
  font-weight: bold;
  color: green;
}

#content{
  padding-top: 90px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="index">
  <div id="index-1">Section One</div>
  <div id="index-2">Section Two</div>
  <div id="index-3">Section Three</div>
  <div id="index-4">Section Four</div>
</div>

<div id="content">
  <div id="1">
    <h1>Section 1</h1>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum quisquam inventore, magnam maxime explicabo maiores reiciendis, repellendus soluta necessitatibus, vero recusandae! Delectus minima, dolore necessitatibus autem. Eveniet harum, asperiores obcaecati.
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit blanditiis suscipit delectus aut neque libero ducimus corporis, cum ipsum a, vitae sed aperiam doloribus quos accusamus harum rem hic totam?
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perferendis, inventore eligendi sed quam omnis ipsam nesciunt quis debitis dignissimos natus molestias rem eos! Fugiat voluptates a nam laudantium porro quisquam!
  </div>
  
    <div id="2">
    <h1>Section Two</h1>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum quisquam inventore, magnam maxime explicabo maiores reiciendis, repellendus soluta necessitatibus, vero recusandae! Delectus minima, dolore necessitatibus autem. Eveniet harum, asperiores obcaecati.
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit blanditiis suscipit delectus aut neque libero ducimus corporis, cum ipsum a, vitae sed aperiam doloribus quos accusamus harum rem hic totam?
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perferendis, inventore eligendi sed quam omnis ipsam nesciunt quis debitis dignissimos natus molestias rem eos! Fugiat voluptates a nam laudantium porro quisquam!
  </div>
  
    <div id="3">
    <h1>Section Three</h1>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum quisquam inventore, magnam maxime explicabo maiores reiciendis, repellendus soluta necessitatibus, vero recusandae! Delectus minima, dolore necessitatibus autem. Eveniet harum, asperiores obcaecati.
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit blanditiis suscipit delectus aut neque libero ducimus corporis, cum ipsum a, vitae sed aperiam doloribus quos accusamus harum rem hic totam?
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perferendis, inventore eligendi sed quam omnis ipsam nesciunt quis debitis dignissimos natus molestias rem eos! Fugiat voluptates a nam laudantium porro quisquam!
  </div>
  
    <div id="4">
    <h1>Section Four</h1>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum quisquam inventore, magnam maxime explicabo maiores reiciendis, repellendus soluta necessitatibus, vero recusandae! Delectus minima, dolore necessitatibus autem. Eveniet harum, asperiores obcaecati.
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit blanditiis suscipit delectus aut neque libero ducimus corporis, cum ipsum a, vitae sed aperiam doloribus quos accusamus harum rem hic totam?
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perferendis, inventore eligendi sed quam omnis ipsam nesciunt quis debitis dignissimos natus molestias rem eos! Fugiat voluptates a nam laudantium porro quisquam!
  </div>
</div>

Credits: isElementInViewport answer taken form this answer

Shaunak
  • 16,227
  • 4
  • 48
  • 81