Issue
I'm looking to add numbers to headers like below. The problem is that there are documents that may be infinitely nested and need to be counted independently. In the original document, the headings are text only, with no numbered prefix.
<div id="text_output">
<h1>Document 1</h1>
<h2>1 Heading</h2>
<h3>1.1 Subheading</h3>
<h3>1.2 Subheading</h3>
<div class="text_output_expansion">
<h1>Document 2</h1>
<h2>1 Heading</h2>
<h3>1.1 Subheading</h3>
<h2>2 Heading</h2>
<h3>2.1 Subheading</h3>
</div>
<h3>1.3 Subheading</h3>
<h2>2 Heading</h2>
<h3>2.1 Subheading</h3>
<h3>2.2 Subheading</h3>
<div class="text_output_expansion">
<h1>Document 3</h1>
<h2>1 Heading</h2>
<h3>1.1 Subheading</h3>
<h2>2 Heading</h2>
<h3>2.1 Subheading</h3>
<div class="text_output_expansion">
<h1>Document 4</h1>
<h2>1 Heading</h2>
<h3>1.1 Subheading</h3>
<h2>2 Heading</h2>
<h3>2.1 Subheading</h3>
</div>
<h3>2.2 Subheading</h3>
</div>
<h3>2.3 Subheading</h3>
<h2>3 Heading</h2>
<h3>3.1 Subheading</h3>
<h3>3.2 Subheading</h3>
</div>
I'll post a CSS solution, but I'm just struggling to get this working in javascript instead. The idea is so that I can copy and paste the document with the numbers intact (CSS solution won't allow that). Admittedly my javascript isn't great, but my struggle seems to be how js loops through the elements from top to bottom.
Solution
You can recursively traverse the structure by grouping on h
tag blocks:
var div = document.createElement("div");
div.innerHTML = `<div id="text_output">
<h1>Document</h1>
<h2>Heading</h2>
<h3>Subheading</h3>
<h3>Subheading</h3>
<div class="text_output_expansion">
<h1>Document</h1>
<h2>Heading</h2>
<h3>Subheading</h3>
<h2>Heading</h2>
<h3>Subheading</h3>
</div>
<h3>Subheading</h3>
<h2>Heading</h2>
<h3>Subheading</h3>
<h3>Subheading</h3>
<div class="text_output_expansion">
<h1>Document</h1>
<h2>Heading</h2>
<h3>Subheading</h3>
<h2>Heading</h2>
<h3>Subheading</h3>
<div class="text_output_expansion">
<h1>Document</h1>
<h2>Heading</h2>
<h3>Subheading</h3>
<h2>Heading</h2>
<h3>Subheading</h3>
</div>
<h3>Subheading</h3>
</div>
<h3>Subheading</h3>
<h2>Heading</h2>
<h3>Subheading</h3>
<h3>Subheading</h3>
</div>`
document.querySelector('body').appendChild(div)
function number_headings(root){
var h1 = 1;
function label_headings(nodes, p){
var [groups, group, h] = [[], [], null]
for (var i of nodes){
if (i.tagName[0].toLowerCase() != 'h' || (h != null && i.tagName != h.tagName)){
group.push(i)
}
else if (h === null){
h = i;
}
else{
groups.push({node:h, block:group.slice()});
group = [];
h = i;
}
}
if (h != null){
groups.push({node:h, block:group.slice()});
}
var c = 1;
for ({node:n, block:b} of groups){
if (n.tagName === 'H1'){
n.textContent = `${n.textContent} ${h1}`;
h1++;
}
else{
n.textContent = `${(p === null ? c.toString() : p+'.'+c.toString())} ${n.textContent}`;
}
label_headings(b, n.tagName === 'H1' ? null : (p === null ? '':p+'.')+c.toString());
for (var k of b.filter(function(x){return x.nodeType === 1 && x.tagName[0] != "H"})){
label_headings(Array.from(k.childNodes).filter(function(x){return x.nodeType === 1}), null);
}
c++;
}
}
label_headings(Array.from(root.childNodes).filter(function(x){return x.nodeType === 1}))
}
number_headings(document.querySelector('#text_output'), null)
Answered By - Ajax1234
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.