For some technical articles, the article directory is needed Aria v1.8.4 This function is added.


preface

Only consider parsing the content of the article on the front end to build a directory. The article referred to earlier is this one JS generated article directory - lxjwlt's blog
The code of this article is quite good, but for many reasons, such as Automatic opening and closing of subdirectories, rolling monitoring and other functions I still feel that my needs are complicated. So I finally chose to use tocbot This project builds the article directory.

Resource introduction

 <script src=" https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.5.0/tocbot.min.js "></script> <link rel="stylesheet" href=" https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.5.0/tocbot.css ">

Notice that in this tutorial, tocbot.min.js The version of must >=4.4.4 This will be explained below

Build Directory

Title Anchor

It should be noted that to make the article directory jump effective by clicking the title h1,h2,h3,h4,h5,h6 The title of must have id This attribute is to provide an anchor point. For example:

 <h1 id="TOC build toc">Build Directory</h1> <h2 id="TOC test">Test</h2> <!-- ... -->

But since we are writing articles on the platform of typecho, naturally most people will give priority to Markdown syntax.

 #Heading 1 ##Heading 2 ###Title 3

However, the default Markdown parser for typecho HyperDown , I was there Parser.php Add to title not found in id The code of this attribute.
In other words, add the title id You have to finish this work yourself.
I use the front end method here, and the back end can also.


Some considerations:

  • It is better not to have symbols or other special characters in the title ID
  • Add an index number to the repeated id to distinguish

For these reasons, there are the following additions id Code of

 var headerEl = 'h1,h2,h3,h4',  //headers  Content='. post content',//article container idArr = {};  // Header array to determine whether to add index id //add #id $(content).children(headerEl).each(function () { //Remove spaces and extra punctuation var headerId = $(this).text().replace(/[\s|\~|`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\||\|\[|\]|\{|\}|\;|\:|\ "|\'|\,|\<|\.|\>|\/|\?|\:|\,|\。]/g, ''); headerId = headerId.toLowerCase(); if (idArr[headerId]) { //ID already exists $(this).attr('id', headerId + '-' + idArr[headerId]); idArr[headerId]++; } else { //ID does not exist idArr[headerId] = 1; $(this).attr('id', headerId); } });

This paragraph is easy to understand. I won't explain it here. Of course, there is more than one way to distinguish duplicate IDs. I just chose a faster and easier way.

initialization

The official simple initialization method:

 tocbot.init({ //Container for building directory tocSelector: '.js-toc', //Article Container contentSelector: '.js-toc-content', //Title to be resolved headingSelector: 'h1, h2, h3', });

According to my requirements, my initialization method is as follows:

 var headerEl = 'h1,h2,h3,h4',  //headers  Content='. post content',//article container idArr = {};  // Header array to determine whether to add index id tocbot.init({ tocSelector: '.toc', contentSelector: content, headingSelector: headerEl, //positionFixedSelector: '.toc', //positionFixedClass: 'is-position-fixed', //fixedSidebarOffset: 'auto', scrollSmooth: true, scrollSmoothOffset: -80, headingsOffset: -500 });

about positionFixedSelector / positionFixedClass / fixedSidebarOffset / scrollSmooth / scrollSmoothOffset / headingsOffset I will analyze these parameters below.

PJAX callback

Here through jquery-pjax Examples:

 $(document).on('pjax:send',function(){ //Destroy () method if ($('.toc').length) tocbot.destroy(); }) $(document).on('pjax:complete',function(){ //Call the toc. init() method again //For example toc.init(tocOptions); })

By now, the basic directory has been built (if not too rough...).

Advanced

Catalog Scroll Follow

The parameters related to this are positionFixedSelector / positionFixedClass / fixedSidebarOffset

positionFixedSelector - Element to add the positionFixedClass to.
positionFixedClass - Fixed position class to add to make sidebar fixed after scrolling down past the fixedSidebarOffset.
fixedSidebarOffset - fixedSidebarOffset can be any number but by default is set to auto which sets the fixedSidebarOffset to the sidebar element's offsetTop from the top of the document on init.

To implement rolling following, you can configure it as follows, for example:

 positionFixedSelector: '.toc', positionFixedClass: 'is-position-fixed', fixedSidebarOffset: 'auto',

The third item is generally set as auto That is, when the directory is not visible, tocbot will be .toc Elements of plus is-position-fixed This one class

 .is-position-fixed { position:fixed !important; top:0 }

of course is-position-fixed You can configure your own CSS. Tocbot defaults to the above.
That is, when the directory is not visible, it will become fixed , follow the screen to scroll.
But I used sticky Method, so I have commented out the above three configurations.
Actually, it is not recommended sticky , according to MDN Statement of

"This is an experimental API. Please try not to use it in the production environment."

However, I prefer to be lazy

Jump offset

For jumping to a certain title, there is a common problem: after jumping to the corresponding title, the floating navigation bar (or other elements) may block your title( #21 )

 Title is blocked

There are two ways to solve this problem:

  1. use scrollEndCallback The configuration item manually writes the code for the offset after the jump
  2. use scrollSmoothOffset This configuration parameter

I used the first method before, but the effect is not good, because after the jump, your scroll bar will roll again to offset. stay tocbot>=4.4.4 After the version, the author of the project added scrollSmoothOffset
The function of this parameter is to offset when jumping, that is to say, jump to the position after the offset directly, rather than to offset after jumping to the title (the scroll bar only scrolls once). It is better than the first method to experience.
This parameter corresponds to the offset Parameter, but the description of this parameter does not appear to be added in the README of the tocbot project.
Pay attention to smoothScroll This item is configured as true

Rolling monitoring

The migration has been completed, but a new problem is encountered:

When scrolling to the corresponding title, the directory will listen to the current position to highlight/highlight the title in the directory. But if the two titles are too close, or you have offset the jump, then The title highlighted in the table of contents may not correspond to the title in the article


For example, the following figure should have been highlighted Analysis of main part 's title (offset), but highlighted Error section This title.
 Screenshot of 2019-03-10 00-10-35. png


At this point, headingsOffset It comes in handy.
 Screenshot of 2019-03-10 00-16-54. png


But how should this value be set? In the tocbot project build-html.js You can see about lines 165-174 in:

 if (heading.offsetTop > top + options.headingsOffset + 10) { // Don't allow negative index value. var index = (i === 0) ?  i : i - 1 topHeader = headings[index] return true } else if (i === headings.length - 1) { // This allows scrolling for the last heading on the page. topHeader = headings[headings.length - 1] return true }

That is to say, satisfaction heading.offsetTop > top + options.headingsOffset + 10 This condition will update the highlighted title. that headingOffset It should be set to a negative value under this requirement. The specific setting of this value can be tried by yourself.

Code Reference

 if ($('.toc').length > 0) { var headerEl = 'h1,h2,h3,h4',  //headers  Content='. post content',//article container idArr = {};  // Header array to determine whether to add index id //add #id $(content).children(headerEl).each(function () { //Remove spaces and extra punctuation var headerId = $(this).text().replace(/[\s|\~|`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\||\|\[|\]|\{|\}|\;|\:|\ "|\'|\,|\<|\.|\>|\/|\?|\:|\,|\。]/g, ''); headerId = headerId.toLowerCase(); if (idArr[headerId]) { //ID already exists $(this).attr('id', headerId + '-' + idArr[headerId]); idArr[headerId]++; } else { //ID does not exist idArr[headerId] = 1; $(this).attr('id', headerId); } }); tocbot.init({ // Where to render the table of contents. tocSelector: '.toc', // Where to grab the headings to build the table of contents. contentSelector: content, // Which headings to grab inside of the contentSelector element. headingSelector: headerEl, //positionFixedSelector: '.toc', //positionFixedClass: 'is-position-fixed', scrollSmooth: true, scrollSmoothOffset: -80, headingsOffset: -500 }); }

Related Links

  1. JS generated article directory - lxjwlt's blog
  2. Add a table of contents for the article