1

I have content consisting of headings (h1, h2, h3, h4, h5, h6).

What I need:

Using jQuery, I need to wrap all elements following each heading until the next same heading or higher heading. By "higher", I mean to say:

  • H5 is "higher" than h6.
  • H4 is "higher" than h5 and h6.
  • H3 is "higher" than h4, h5, and h6.

...and so on, so forth.

E.g. Wrap all elements after h3 until the next h3 or higher headings (h1 or h2).

Here's an example of the desired end result:

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: Arial;
  color: white;
  background-color: black;
  margin: 0;
  padding: 5px;
}

.h1-section,
.h2-section,
.h3-section,
.h4-section,
.h5-section,
.h6-section {
  border: 1px solid black;
  border-top: none;
  padding: 20px;
}
<h1>1</h1>
<section class="h1-section">
  <h2>2</h2>
  <section class="h2-section">
    <h3>3</h3>
    <section class="h3-section">
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
    </section>
  </section>
</section>
<h1>1</h1>
<section class="h1-section">
  <h2>2</h2>
  <section class="h2-section">
    <h3>3</h3>
    <section class="h3-section">
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
    </section>
    <h3>3</h3>
    <section class="h3-section">
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
    </section>
  </section>
  <h2>2</h2>
  <section class="h2-section">
    <h3>3</h3>
    <section class="h3-section">
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
    </section>
    <h3>3</h3>
    <section class="h3-section">
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
      <h4>4</h4>
      <section class="h4-section">
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
        <h5>5</h5>
        <section class="h5-section">
          <h6>6</h6>
          <section class="h6-section">
          </section>
          <h6>6</h6>
          <section class="h6-section">
          </section>
        </section>
      </section>
    </section>
  </section>
</section>

What I've tried:

Using jQuery, I've tried using .nextUntil(), but I'm incorrectly selecting and wrapping elements:

$(document).ready(function() {
  $("h1").nextUntil("h1").wrapAll("<section class='h1-section'></section>");
  $("h2").nextUntil("h1, h2").wrapAll("<section class='h2-section'></section>");
  $("h3").nextUntil("h1, h2, h3").wrapAll("<section class='h3-section'></section>");
  $("h4").nextUntil("h1, h2, h3, h4").wrapAll("<section class='h4-section'></section>");
  $("h5").nextUntil("h1, h2, h3, h4, h5").wrapAll("<section class='h5-section'></section>");
  $("h6").nextUntil("h1, h2, h3, h4, h5, h6").wrapAll("<section class='h6-section'></section>");
});
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: Arial;
  color: white;
  background-color: black;
  margin: 0;
  padding: 5px;
}

.h1-section,
.h2-section,
.h3-section,
.h4-section,
.h5-section,
.h6-section {
  border: 1px solid black;
  border-top: none;
  padding: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>1</h1>
<h2>2</h2>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h1>1</h1>
<h2>2</h2>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h2>2</h2>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>
<h5>5</h5>
<h6>6</h6>
<h6>6</h6>

How can I achieve the desired outcome using jQuery?

Clarus Dignus
  • 3,847
  • 3
  • 31
  • 57

1 Answers1

1

The issue is that something like:

$("h5").nextUntil("h5")

will return all of the nextUntils, not just the ones for each h5 that you're expecting. ie it will get the nextUntils for the first h5 and combine them with the nextUntils of the second h5, etc then when you apply wrapAll, it will wrapAll all of them, not just the individual sets.

Example using .text() to show this

console.log($("h5").nextUntil("h5").length, $("h5").nextUntil("h5").text())
h5,h6 { margin:0; padding:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h5></h5><h6>1</h6><h6>2</h6>
<h5></h5><h6>3</h6><h6>4</h6>

So you need to look at each h5 one at a time

$("h5").each(function() {
    console.log($(this).nextUntil("h5").length, $(this).nextUntil("h5").text())
});
h5,h6 { margin:0; padding:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h5></h5><h6>1</h6><h6>2</h6>
<h5></h5><h6>3</h6><h6>4</h6>

Then you can replace the .length / .text() with your .wrapAll():

$("h5").each(function() {
    $(this).nextUntil("h5").wrapAll("<div class='section-h5'>")
});
$("h6").each(function() {
    $(this).nextUntil("h6,h5").wrapAll("<div class='section-h6'>")
});
h5,h6 { margin:0; padding:5px; }
h5 {  margin-top:4px;  }
h6 { }

.section-h5 { padding: 5px; border:1px solid blue; }
.section-h6 { padding: 5px; border:1px solid green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h5></h5><h6></h6><div>1</div><h6></h6><div>2</div>
<h5></h5><h6></h6><div>3</div><h6></h6><div>4</div>

(extend to other H levels as required)

freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • 1
    Thank you for rectifying my misunderstanding of `nextUntil()`. I didn't realise that it would combine all elements following each selected heading. Thank you for breaking the solution down using `.length` and `text()`. I've extended your solution to all headings as required: https://jsfiddle.net/Clarus_Dignus/xpvt214o/4292/ – Clarus Dignus Mar 29 '18 at 17:20