What is TOC? table of content.
What's the specific effect? You can find a hexo blog to experience it, such as this one.
OK, there are 2 key points to implementing it:
Point directory to jump to paragraph: implemented through the anchor point of the <a> tag, its principle ishere。
Scroll triggers directory transformation: listen to scroll events through js, determine the current paragraph, and highlight the corresponding directory item.
I wrote a simple demo to demonstrate this effect.
Source code address:/owenliang/js-toc
Online experience:/js-toc
Implement analysis
#toc is the directory on the left, and #content is the article text on the right.
<div > <ul> </ul> </div> <div > <a name="seg-1" class="seg-begin"><h1>The1chapter</h1></a> <div class="seg-content"></div> <a name="seg-2" class="seg-begin"><h1>The2chapter</h1></a> <div class="seg-content"></div> <a name="seg-3" class="seg-begin"><h1>The3chapter</h1></a> <div class="seg-content"></div> <a name="seg-4" class="seg-begin"><h1>The4chapter</h1></a> <div class="seg-content"></div> </div>
Use css to control #toc to the left, the current directory is highlighted in red, and the main text is filled with the screen on the right:
#toc { width: 200px; position: fixed; left: 0; top: 0; } #toc { color: red; } #content { margin-left: 200px; }
In the above static page, the directory is temporarily empty because it needs to be generated dynamically in JS.
The text requires manual burying of the paragraph starting mark, that is, an anchor point like -begin. The anchor point name of each paragraph is unique, and the anchor point is followed by the content of the paragraph.
In JS, I first collect all the -begins in the order of occurrence of anchor points and save them into the segs array. The order is the top-down reading order of the article, and create a <ul> list in #toc according to the paragraph title in its <h1>:
var segs = []; $(".seg-begin").each(function (idx, node) { (node) var link = $("<a></a>").attr("href", "#" + $(node).attr("name")).html($(node).children("h1").html()) if (!idx) { ("active") } var row = $("<li></li>").append(link) $("#toc ul").append(row) })
Then bind the browser's scroll event to listen. Each time you scroll, you will determine that the most recent -begin node rolled out of the top of the screen, which is the paragraph you are currently reading:
$(window).bind("scroll", function() { var scrollTop = $(this).scrollTop() var topSeg = null for (var idx in segs) { var seg = segs[idx] if ( > scrollTop) { continue } if (!topSeg) { topSeg = seg } else if ( >= ) { topSeg = seg } } if (topSeg) { $("#toc a").removeClass("active") var link = "#" + $(topSeg).attr("name") ('#toc a[href="' + link + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ]') $('#toc a[href="' + link + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ]').addClass("active") // ($(topSeg).children("h1").text()) } })
Subsequent
The generation of the directory here is dynamically generated in the front-end JS based on the anchor points of the text. For SEO, these anchor points can be matched when submitting the article body in the back-end and saved directly as a directory.
Complete code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> * { margin: 0; padding: 0; word-break: break-all; } #toc { width: 200px; position: fixed; left: 0; top: 0; } #toc { color: red; } #content { margin-left: 200px; } </style> <script src="/jquery/3.2.1/"></script> <script> $(document).ready(function () { for (var i = 0; i < 50; ++i) { $(".seg-content").append("<p>just a paragraph</p>") } (function () { var segs = []; $(".seg-begin").each(function (idx, node) { (node) var link = $("<a></a>").attr("href", "#" + $(node).attr("name")).html($(node).children("h1").html()) if (!idx) { ("active") } var row = $("<li></li>").append(link) $("#toc ul").append(row) }) $(window).bind("scroll", function() { var scrollTop = $(this).scrollTop() var topSeg = null for (var idx in segs) { var seg = segs[idx] if ( > scrollTop) { continue } if (!topSeg) { topSeg = seg } else if ( >= ) { topSeg = seg } } if (topSeg) { $("#toc a").removeClass("active") var link = "#" + $(topSeg).attr("name") ('#toc a[href="' + link + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ]') $('#toc a[href="' + link + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ]').addClass("active") // ($(topSeg).children("h1").text()) } }) })() }) </script> </head> <body> <div > <ul> </ul> </div> <div > <a name="seg-1" class="seg-begin"><h1>The1chapter</h1></a> <div class="seg-content"></div> <a name="seg-2" class="seg-begin"><h1>The2chapter</h1></a> <div class="seg-content"></div> <a name="seg-3" class="seg-begin"><h1>The3chapter</h1></a> <div class="seg-content"></div> <a name="seg-4" class="seg-begin"><h1>The4chapter</h1></a> <div class="seg-content"></div> </div> </body> </html>
In addition, there is no nested directory structure implemented here. I have observed the method of hexo, which expresses hierarchy through h1, h2, and h3. In this way, when each traversal to generate directories, the nested hierarchy can be completed based on this information, and the problem is solved.