Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

266
Vistas
Numbering h1,h2 headings, nested documents that reset counter

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.

about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

One solution is to use CSS counters to append text to the headers instead. Unfortunately, the weakness (or strength) of this approach is that it is visible to the user, but it cannot be copy pasted from the browser, nor is it easy to get the value if you need it elsewhere in your script.

#text_output {
  counter-reset: h2
}

#text_output h2 {
  counter-reset: h3
}

#text_output .text_output_expansion .card-body{
  counter-reset: h2
}

#text_output .text_output_expansion .card-body h2 {
  counter-reset: h3
}

#text_output h2:before {
  counter-increment: h2;
  content: counter(h2) ". "
}

#text_output h3:before,
h3.md-focus.md-heading:before {
  counter-increment: h3;
  content: counter(h2) "."counter(h3) ". "
}

about 4 years ago · Juan Pablo Isaza Denunciar

0

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)
about 4 years ago · Juan Pablo Isaza Denunciar

0

Did another recursive option. I think Ajax has the best answer tho!

function numberHeadings(element) {
var h1_counter = 0;
var h2_counter = 0;
var h3_counter = 0;
var h4_counter = 0;
var h5_counter = 0;
var h6_counter = 0;
element.find(".text_output_expansion").each(function () {
  numberHeadings($(this));
});

element.find("h1:not([numberHeadings=complete]),h2:not([numberHeadings=complete]),h3:not([numberHeadings=complete]),h4:not([numberHeadings=complete]),h5:not([numberHeadings=complete]),h6:not([numberHeadings=complete])").each(function () {
  switch ($(this).prop("tagName")) {
    case "H1":
      h1_counter += 1;
      h2_counter = 0;
      h3_counter = 0;
      h4_counter = 0;
      h5_counter = 0;
      h6_counter = 0;
      $(this).text(h1_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
    case "H2":
      h2_counter += 1;
      h3_counter = 0;
      h4_counter = 0;
      h5_counter = 0;
      h6_counter = 0;
      $(this).text(h1_counter.toString() + "." + h2_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
    case "H3":
      h3_counter += 1;
      h4_counter = 0;
      h5_counter = 0;
      h6_counter = 0;
      $(this).text(h1_counter.toString() + "." + h2_counter.toString() + "." + h3_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
    case "H4":
      h4_counter += 1;
      h5_counter = 0;
      h6_counter = 0;
      $(this).text(h1_counter.toString() + "." + h2_counter.toString() + "." + h3_counter.toString() + "." + h4_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
    case "H5":
      h5_counter += 1;
      h6_counter = 0;
      $(this).text(h1_counter.toString() + "." + h2_counter.toString() + "." + h3_counter.toString() + "." + h4_counter.toString() + "." + h5_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
    case "H6":
      h6_counter += 1;
      $(this).text(h1_counter.toString() + "." + h2_counter.toString() + "." + h3_counter.toString() + "." + h4_counter.toString() + "." + h5_counter.toString() + "." + h6_counter.toString() + ". " + $(this).text());
      $(this).attr("numberHeadings", "complete");
      break;
  }
})

};

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda