Javascript for numbered table of contents

253
August 30, 2017, at 6:28 PM

I am putting together a Sharepoint wiki and on each article I have a wikipedia like table of contents from the headings using what I presume is a javascript that I found on the InterWebz.

<style>
#toc {
 display: table;
 border: 1px solid #aaa;
 background-color: #f9f9f9;
 font-size: 95%;
 padding: 7px;
}
#toc #tocHeader  {
 font-weight: bold;
 text-align: center;
}
#toc a:before { /* content:"• "; */ }
#toc a { line-height: 15px; margin: 10px; }
#toc .toc_Level1 { margin-left: 5px; }
#toc .toc_Level2 { margin-left: 15px; }
#toc .toc_Level3 { margin-left: 25px; }
#toc .toc_Level4 { margin-left: 35px; }
</style> 
<div id="toc"></div> 
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<script type="text/javascript"> 
function GenerateTOC() {
  $("#toc").append('<p id="tocHeader">Contents</p>');
  $(".ms-rteElement-H1, .ms-rteElement-H1B, .ms-rteElement-H2, .ms-rteElement-H2B, .ms-rteElement-H3, .ms-rteElement-H3B, .ms-rteElement-H4, .ms-rteElement-H4B").each(function(i) {
 var currentNode = $(this);
  currentNode.attr("id", "title" + i);
 var linkClass = (currentNode.hasClass('ms-rteElement-H1') || currentNode.hasClass('ms-rteElement-H1B'))
   ? "toc_Level1" 
   : (currentNode.hasClass('ms-rteElement-H2') || currentNode.hasClass('ms-rteElement-H2B'))
   ? "toc_Level2" 
   : (currentNode.hasClass('ms-rteElement-H3') || currentNode.hasClass('ms-rteElement-H3B'))
   ? "toc_Level3" 
   : (currentNode.hasClass('ms-rteElement-H4') || currentNode.hasClass('ms-rteElement-H4B'))
   ? "toc_Level4" 
   : "";
  $("#toc").append("<a id='link'" + i + "' class='" + linkClass + "' href='#title" + i + "' title='" + currentNode.attr("tagName") + "'>" + currentNode.html() + "</a><br>");
  currentNode.append(" <a href='#tocHeader'>[top]</a>");
  });
}
_spBodyOnLoadFunctionNames.push('GenerateTOC');
</script>

My intention is to make the table of contents numbered separately for each level of indentation, e.g.:

1. Heading 1   
  1.1. Heading 2    
  1.2. Heading 2   
2. Heading 1
  2.1. Heading 2   
  2.2. Heading 2

Although I more or less understand what is happening in this code I have absolutely no knowledge of javascript (or web development for that matter - I have no idea how I got to what I am currently doing now to be honest), so I have very little idea how to implement it. I can see the function already has a counter to generate the internal link.

My strategy would be to put in two additional variables, say J and K (I have only two levels of indent), where J would increment only when it is level 1 heading (Element-H1, toc_Level1) and K would increment always but would get reset to 0 whenever level 1 heading (Element-H1, toc_Level1) is processed, however I have absolutely no idea how to make that happen.

EDIT:

I added the remaining parts of the javascript, however they only define the formatting of the table of contents, so I did not deem them relevant at first.

The website is structured as follows:

<span class="ms-rteElement-Name">NAME</span>
<hr/>
<span class="ms-rteElement-Section">Section1</span>
<hr/>
<span class="ms-rteStyle-Text">Stuff</span>
<p><span class="ms-rteElement-Section">Section2</span></p>
<hr/>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H1">Heading 1</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H2">Heading 2</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H2">Heading 2</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H1">Heading 1</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H2">Heading 2</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<p><span class="ms-rteElement-H2">Heading 2</span></p>
<p><span class="ms-rteStyle-Text">Fluff</span></p>
<span class="ms-rteElement-Section">Section3</span>
<hr/>
<p><span class="ms-rteStyle-Text">More fluff</span></p>

The script as given above generates the table of contents perfectly, just without the preceeding numbers of course.

Answer 1

A possible solution

function GenerateTOC() {
  $("#toc").append('<p id="tocHeader">Contents</p>');
  $(".ms-rteElement-H1, .ms-rteElement-H1B, .ms-rteElement-H2, .ms-rteElement-H2B, .ms-rteElement-H3, .ms-rteElement-H3B, .ms-rteElement-H4, .ms-rteElement-H4B").each(function(i) {
    var currentNode = $(this);
    currentNode.attr("id", "title" + i);
    var linkClass = (currentNode.hasClass('ms-rteElement-H1') || currentNode.hasClass('ms-rteElement-H1B')) ?
      "toc_Level1" :
      (currentNode.hasClass('ms-rteElement-H2') || currentNode.hasClass('ms-rteElement-H2B')) ?
      "toc_Level2" :
      (currentNode.hasClass('ms-rteElement-H3') || currentNode.hasClass('ms-rteElement-H3B')) ?
      "toc_Level3" :
      (currentNode.hasClass('ms-rteElement-H4') || currentNode.hasClass('ms-rteElement-H4B')) ?
      "toc_Level4" :
      "";
    $("#toc").append("<a id='link'" + i + "' class='" + linkClass + "' href='#title" + i + "' title='" + currentNode.attr("tagName") + "'>" + currentNode.html() + "</a><br>");
    currentNode.append(" <a href='#tocHeader'>[top]</a>");
  });
  // level 1
  $(".ms-rteElement-H1, .ms-rteElement-H1B").each(function(index, el) {
    $(this).html(index + '. ' + $(this).text());
    // level 2
    $('.ms-rteElement-H2, .ms-rteElement-H2B', this).each(function(idx, child) {
      $(this).html(index + '.' + idx + ' ' + $(this).text());
    });
  });
}
Answer 2

A solution that generates an ordered list, which should be able to work with deeper documents:

function levelClassName(level) { 
  if (typeof level !== "number") { 
    throw new Error("Wrong argument type"); 
  } 
 
  return "ms-rteElement-H".concat(level.toString(10)); 
} 
 
function elementLink(element) { 
  return $("<li></li>").text($(element).text()); 
} 
 
function generateTOC() { 
  var level = 1, // list level 
    tocList = $("<ol></ol>"), // top level list 
    lastElement = tocList; 
 
  $("#toc").append('<p id="tocHeader">Contents</p>'); 
  $("#toc").append(tocList); 
 
  $("span").each(function(index, value) { 
    // make a jQuery object out of current element once instead of doing it 
    // every time I want to use it 
    var element = $(value), 
      newElement; 
 
    if (element.hasClass(levelClassName(level))) { // same level 
      newElement = elementLink(value); 
      // append inside list tag or next to last tag if it isn't a list 
      lastElement[lastElement.prop("tagName") === "OL" ? 
        "append" : 
        "after"](newElement); 
      lastElement = newElement; 
    } else if (element.hasClass(levelClassName(level + 1))) { // deeper 
      newElement = elementLink(value); 
      // make a new list tag for the deeper level 
      lastElement.append($("<ol></ol>").append(newElement)); 
      lastElement = newElement; 
      level += 1; 
    } else if (level - 1 >= 1 && // shallower 
      element.hasClass(levelClassName(level - 1))) { 
      newElement = elementLink(value); 
      lastElement // list element 
        .parent().parent() // -> list (on same level) -> list element above 
        .after(newElement); 
      lastElement = newElement; 
      level -= 1; 
    } 
  }); 
} 
 
$(document).ready(generateTOC);
#toc { 
  display: table; 
  border: 1px solid #aaa; 
  background-color: #f9f9f9; 
  font-size: 95%; 
  padding: 7px; 
} 
 
#toc #tocHeader { 
  font-weight: bold; 
  text-align: center; 
} 
 
#toc a:before { 
  /* content:"• "; */ 
} 
 
#toc a { 
  line-height: 15px; 
  margin: 10px; 
} 
 
#toc .toc_Level1 { 
  margin-left: 5px; 
} 
 
#toc .toc_Level2 { 
  margin-left: 15px; 
} 
 
#toc .toc_Level3 { 
  margin-left: 25px; 
} 
 
#toc .toc_Level4 { 
  margin-left: 35px; 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
<div id="toc"></div> 
<span class="ms-rteElement-Name">NAME</span> 
<hr/> 
<span class="ms-rteElement-Section">Section1</span> 
<hr/> 
<span class="ms-rteStyle-Text">Stuff</span> 
<p><span class="ms-rteElement-Section">Section2</span></p> 
<hr/> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H1">Heading 1</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H2">Heading 2</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H2">Heading 2</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H1">Heading 1</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H2">Heading 2</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<p><span class="ms-rteElement-H2">Heading 2</span></p> 
<p><span class="ms-rteStyle-Text">Fluff</span></p> 
<span class="ms-rteElement-Section">Section3</span> 
<hr/> 
<p><span class="ms-rteStyle-Text">More fluff</span></p>

There is no code that makes links clickable. The best way to do that would be to give unique ids to every heading and then link to them in ToC.

Rent Charter Buses Company
READ ALSO
Bootstrap checkbuttons with icons does not works

Bootstrap checkbuttons with icons does not works

I have the need to add checkbuttons to my table and I want use the Bootstrap Button for this

302
how to select and extract texts between two elements?

how to select and extract texts between two elements?

I am trying to scrape this website using scrapyThe page structure looks like this:

275
How can I change the color of the text in the tab of a QTabWidget

How can I change the color of the text in the tab of a QTabWidget

I'm not talking about the stuff within the tab, but the tab itselfAside from it looking weird, I'd like to closely emulate Mac's native look

637
How to render an object from SVG in UIWebview along JQuery code?

How to render an object from SVG in UIWebview along JQuery code?

I loaded a SVG image into UIWebView and it works fineBut when I try to render an object from UIWebView I can't get the object

297