Take a look at this library - https://www.cssscript.com/generating-a-table-of-contents-with-pure-javascript-toc/
You can simply place the js code in your controller and it will place all the headers under a tag with id #toc
in your html file create
<div class="col-md-3 col-xs-12">
<aside id="toc"></aside> <!-- Content will appear here -->
</div>
<div class="col-md-9 col-xs-12 no-padding">
<div id="doc-content">
... Your content is here ...
</div>
And then in your controller include this function. You can call it from onInit for example.
loadTOC(){
// Definitions
var extendObj = function (src, target) {
for (var prop in target) {
if (target.hasOwnProperty(prop) && target[prop]) {
src[prop] = target[prop];
}
}
return src;
};
var getHeaders = function (selector, scope) {
var ret = [];
var target = document.querySelectorAll(scope);
Array.prototype.forEach.call(target, function (elem) {
var elems = elem.querySelectorAll(selector);
ret = ret.concat(Array.prototype.slice.call(elems));
});
return ret;
};
var getLevel = function (header) {
if (typeof header !== 'string') {
return 0;
}
var decs = header.match(/\d/g);
return decs ? Math.min.apply(null, decs) : 1;
};
var createList = function (wrapper, count) {
while (count--) {
wrapper = wrapper.appendChild(
document.createElement('ol')
);
if (count) {
wrapper = wrapper.appendChild(
document.createElement('li')
);
}
}
return wrapper;
};
var jumpBack = function (currentWrapper, offset) {
while (offset--) {
currentWrapper = currentWrapper.parentElement;
}
return currentWrapper;
};
var setAttrs = function (overwrite, prefix) {
return function (src, target, index) {
var content = src.textContent;
var pre = prefix + '-' + index;
target.textContent = content;
var id = overwrite ? pre : (src.id || pre);
id = encodeURIComponent(id);
src.id = id;
target.href = '#' + id;
};
};
var buildTOC = function (options) {
var selector = options.selector;
var scope = options.scope;
var ret = document.createElement('ol');
var wrapper = ret;
var lastLi = null;
var _setAttrs = setAttrs(options.overwrite, options.prefix);
getHeaders(selector, scope).reduce(function (prev, cur, index) {
var currentLevel = getLevel(cur.tagName);
var offset = currentLevel - prev;
if (offset > 0) {
wrapper = createList(lastLi, offset);
}
if (offset < 0) {
wrapper = jumpBack(wrapper, -offset * 2);
}
wrapper = wrapper || ret;
var li = document.createElement('li');
var a = document.createElement('a');
_setAttrs(cur, a, index);
wrapper.appendChild(li).appendChild(a);
lastLi = li;
return currentLevel;
}, getLevel(selector));
return ret;
};
var initTOC = function (options) {
var defaultOpts = {
selector: 'h1, h2, h3, h4, h5, h6',
scope: 'body',
overwrite: false,
prefix: 'toc'
};
options = extendObj(defaultOpts, options);
var selector = options.selector;
if (typeof selector !== 'string') {
throw new TypeError('selector must be a string');
}
if (!selector.match(/^(?:h[1-6],?\s*)+$/g)) {
throw new TypeError('selector must contains only h1-6');
}
return buildTOC(options);
};
// Generating the TOC
var container = document.querySelector('#toc');
var toc = initTOC({
selector: 'h1, h2',
scope: '#doc-content', // you can specify here a tag where to look at
overwrite: false,
prefix: 'toc'
});
container.appendChild(toc);
}